aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorTony Puccinelli2010-08-10 23:21:08 +0000
committerTony Puccinelli2010-08-10 23:21:08 +0000
commit682807f0e916b189c69b60765418ee1ccc327cbf (patch)
treee8b220b934f6b14d4dac290181fce0c1b209579a /engines
parent83f1531cb8fa5ba43c246143ecdde2cb1d140a8e (diff)
parentfffec23a02cc88ed8daba0a3b50007b7e220c075 (diff)
downloadscummvm-rg350-682807f0e916b189c69b60765418ee1ccc327cbf.tar.gz
scummvm-rg350-682807f0e916b189c69b60765418ee1ccc327cbf.tar.bz2
scummvm-rg350-682807f0e916b189c69b60765418ee1ccc327cbf.zip
merged trunk into branch, reverted Cruise Singleton changes
svn-id: r51961
Diffstat (limited to 'engines')
-rw-r--r--engines/advancedDetector.cpp69
-rw-r--r--engines/advancedDetector.h22
-rw-r--r--engines/agi/agi.cpp42
-rw-r--r--engines/agi/agi.h295
-rw-r--r--engines/agi/console.cpp26
-rw-r--r--engines/agi/console.h17
-rw-r--r--engines/agi/cycle.cpp25
-rw-r--r--engines/agi/detection.cpp847
-rw-r--r--engines/agi/detection_tables.h863
-rw-r--r--engines/agi/keyboard.cpp11
-rw-r--r--engines/agi/loader_v2.cpp2
-rw-r--r--engines/agi/loader_v3.cpp18
-rw-r--r--engines/agi/module.mk5
-rw-r--r--engines/agi/op_cmd.cpp1432
-rw-r--r--engines/agi/op_test.cpp101
-rw-r--r--engines/agi/picture.cpp14
-rw-r--r--engines/agi/preagi.cpp4
-rw-r--r--engines/agi/saveload.cpp10
-rw-r--r--engines/agi/sound.cpp1209
-rw-r--r--engines/agi/sound.h425
-rw-r--r--engines/agi/sound_2gs.cpp919
-rw-r--r--engines/agi/sound_2gs.h353
-rw-r--r--engines/agi/sound_coco3.cpp80
-rw-r--r--engines/agi/sound_coco3.h73
-rw-r--r--engines/agi/sound_midi.cpp345
-rw-r--r--engines/agi/sound_midi.h114
-rw-r--r--engines/agi/sound_pcjr.cpp512
-rw-r--r--engines/agi/sound_pcjr.h127
-rw-r--r--engines/agi/sound_sarien.cpp357
-rw-r--r--engines/agi/sound_sarien.h120
-rw-r--r--engines/agi/sprite.cpp67
-rw-r--r--engines/agi/sprite.h4
-rw-r--r--engines/agi/text.cpp14
-rw-r--r--engines/agi/view.cpp36
-rw-r--r--engines/agi/view.h1
-rw-r--r--engines/agi/wagparser.cpp6
-rw-r--r--engines/agi/words.cpp6
-rw-r--r--engines/agos/agos.cpp55
-rw-r--r--engines/agos/agos.h2
-rw-r--r--engines/agos/cursor.cpp18
-rw-r--r--engines/agos/detection.cpp11
-rw-r--r--engines/agos/draw.cpp8
-rw-r--r--engines/agos/event.cpp11
-rw-r--r--engines/agos/gfx.cpp6
-rw-r--r--engines/agos/icons.cpp2
-rw-r--r--engines/agos/input.cpp18
-rw-r--r--engines/agos/midi.cpp14
-rw-r--r--engines/agos/verb.cpp6
-rw-r--r--engines/agos/vga_e2.cpp6
-rw-r--r--engines/agos/vga_s2.cpp2
-rw-r--r--engines/cine/anim.cpp16
-rw-r--r--engines/cine/anim.h2
-rw-r--r--engines/cine/bg_list.cpp21
-rw-r--r--engines/cine/bg_list.h1
-rw-r--r--engines/cine/cine.cpp48
-rw-r--r--engines/cine/cine.h35
-rw-r--r--engines/cine/detection.cpp499
-rw-r--r--engines/cine/detection_tables.h513
-rw-r--r--engines/cine/gfx.cpp137
-rw-r--r--engines/cine/gfx.h4
-rw-r--r--engines/cine/main_loop.cpp28
-rw-r--r--engines/cine/msg.cpp10
-rw-r--r--engines/cine/msg.h2
-rw-r--r--engines/cine/object.cpp85
-rw-r--r--engines/cine/object.h3
-rw-r--r--engines/cine/pal.cpp23
-rw-r--r--engines/cine/pal.h2
-rw-r--r--engines/cine/part.cpp54
-rw-r--r--engines/cine/part.h2
-rw-r--r--engines/cine/prc.cpp13
-rw-r--r--engines/cine/prc.h3
-rw-r--r--engines/cine/rel.cpp16
-rw-r--r--engines/cine/rel.h2
-rw-r--r--engines/cine/saveload.cpp104
-rw-r--r--engines/cine/saveload.h2
-rw-r--r--engines/cine/script.h3
-rw-r--r--engines/cine/script_fw.cpp73
-rw-r--r--engines/cine/script_os.cpp12
-rw-r--r--engines/cine/various.cpp314
-rw-r--r--engines/cine/various.h6
-rw-r--r--engines/cruise/actor.cpp28
-rw-r--r--engines/cruise/background.cpp12
-rw-r--r--engines/cruise/cruise.cpp10
-rw-r--r--engines/cruise/cruise.h20
-rw-r--r--engines/cruise/cruise_main.cpp50
-rw-r--r--engines/cruise/cruise_main.h2
-rw-r--r--engines/cruise/ctp.cpp10
-rw-r--r--engines/cruise/ctp.h3
-rw-r--r--engines/cruise/decompiler.cpp2
-rw-r--r--engines/cruise/detection.cpp6
-rw-r--r--engines/cruise/font.cpp27
-rw-r--r--engines/cruise/function.cpp12
-rw-r--r--engines/cruise/gfxModule.cpp97
-rw-r--r--engines/cruise/gfxModule.h6
-rw-r--r--engines/cruise/mainDraw.cpp26
-rw-r--r--engines/cruise/perso.cpp13
-rw-r--r--engines/cruise/saveload.cpp20
-rw-r--r--engines/cruise/script.cpp4
-rw-r--r--engines/cruise/vars.cpp37
-rw-r--r--engines/cruise/vars.h63
-rw-r--r--engines/cruise/volume.cpp62
-rw-r--r--engines/dialogs.cpp64
-rw-r--r--engines/dialogs.h43
-rw-r--r--engines/draci/animation.cpp6
-rw-r--r--engines/draci/barchive.cpp5
-rw-r--r--engines/draci/detection.cpp16
-rw-r--r--engines/draci/draci.cpp78
-rw-r--r--engines/draci/game.cpp151
-rw-r--r--engines/draci/game.h20
-rw-r--r--engines/draci/module.mk3
-rw-r--r--engines/draci/script.cpp8
-rw-r--r--engines/draci/sound.cpp203
-rw-r--r--engines/draci/sound.h130
-rw-r--r--engines/draci/walking.cpp4
-rw-r--r--engines/drascula/actors.cpp11
-rw-r--r--engines/drascula/animation.cpp142
-rw-r--r--engines/drascula/console.cpp (renamed from engines/mohawk/myst_pict.h)54
-rw-r--r--engines/drascula/console.h (renamed from engines/mohawk/jpeg.h)38
-rw-r--r--engines/drascula/converse.cpp10
-rw-r--r--engines/drascula/detection.cpp6
-rw-r--r--engines/drascula/drascula.cpp47
-rw-r--r--engines/drascula/drascula.h5
-rw-r--r--engines/drascula/graphics.cpp25
-rw-r--r--engines/drascula/interface.cpp8
-rw-r--r--engines/drascula/module.mk1
-rw-r--r--engines/drascula/objects.cpp3
-rw-r--r--engines/drascula/palette.cpp4
-rw-r--r--engines/drascula/rooms.cpp4
-rw-r--r--engines/drascula/talk.cpp39
-rw-r--r--engines/engine.cpp3
-rw-r--r--engines/game.cpp4
-rw-r--r--engines/game.h1
-rw-r--r--engines/gob/demos/demoplayer.cpp103
-rw-r--r--engines/gob/demos/demoplayer.h4
-rw-r--r--engines/gob/detection.cpp4995
-rw-r--r--engines/gob/detection_tables.h5013
-rw-r--r--engines/gob/draw.cpp12
-rw-r--r--engines/gob/draw.h7
-rw-r--r--engines/gob/draw_fascin.cpp12
-rw-r--r--engines/gob/draw_playtoons.cpp10
-rw-r--r--engines/gob/draw_v1.cpp10
-rw-r--r--engines/gob/draw_v2.cpp30
-rw-r--r--engines/gob/game.cpp18
-rw-r--r--engines/gob/gob.cpp13
-rw-r--r--engines/gob/hotspots.cpp2
-rw-r--r--engines/gob/init.cpp8
-rw-r--r--engines/gob/inter_bargon.cpp155
-rw-r--r--engines/gob/inter_fascin.cpp40
-rw-r--r--engines/gob/inter_playtoons.cpp4
-rw-r--r--engines/gob/inter_v1.cpp25
-rw-r--r--engines/gob/inter_v2.cpp78
-rw-r--r--engines/gob/inter_v3.cpp4
-rw-r--r--engines/gob/inter_v4.cpp97
-rw-r--r--engines/gob/inter_v6.cpp96
-rw-r--r--engines/gob/mult.cpp6
-rw-r--r--engines/gob/mult_v1.cpp8
-rw-r--r--engines/gob/mult_v2.cpp95
-rw-r--r--engines/gob/scenery.cpp58
-rw-r--r--engines/gob/totfile.cpp2
-rw-r--r--engines/gob/videoplayer.cpp1049
-rw-r--r--engines/gob/videoplayer.h191
-rw-r--r--engines/groovie/cell.h6
-rw-r--r--engines/groovie/cursor.cpp13
-rw-r--r--engines/groovie/cursor.h3
-rw-r--r--engines/groovie/debug.cpp6
-rw-r--r--engines/groovie/debug.h4
-rw-r--r--engines/groovie/detection.cpp10
-rw-r--r--engines/groovie/font.cpp158
-rw-r--r--engines/groovie/font.h36
-rw-r--r--engines/groovie/graphics.cpp3
-rw-r--r--engines/groovie/graphics.h2
-rw-r--r--engines/groovie/groovie.cpp51
-rw-r--r--engines/groovie/groovie.h21
-rw-r--r--engines/groovie/music.cpp101
-rw-r--r--engines/groovie/music.h13
-rw-r--r--engines/groovie/player.cpp2
-rw-r--r--engines/groovie/resource.cpp2
-rw-r--r--engines/groovie/roq.cpp3
-rw-r--r--engines/groovie/script.cpp68
-rw-r--r--engines/groovie/script.h15
-rw-r--r--engines/groovie/vdx.cpp125
-rw-r--r--engines/groovie/vdx.h4
-rw-r--r--engines/kyra/debugger.cpp4
-rw-r--r--engines/kyra/detection.cpp1178
-rw-r--r--engines/kyra/detection_tables.h1209
-rw-r--r--engines/kyra/gui_lok.cpp2
-rw-r--r--engines/kyra/gui_lol.cpp4
-rw-r--r--engines/kyra/gui_v2.cpp2
-rw-r--r--engines/kyra/kyra_hof.cpp5
-rw-r--r--engines/kyra/kyra_lok.cpp1
-rw-r--r--engines/kyra/kyra_lok.h35
-rw-r--r--engines/kyra/kyra_mr.cpp11
-rw-r--r--engines/kyra/kyra_mr.h1
-rw-r--r--engines/kyra/kyra_v1.cpp30
-rw-r--r--engines/kyra/kyra_v2.cpp1
-rw-r--r--engines/kyra/kyra_v2.h1
-rw-r--r--engines/kyra/lol.cpp6
-rw-r--r--engines/kyra/lol.h2
-rw-r--r--engines/kyra/module.mk1
-rw-r--r--engines/kyra/resource.h1
-rw-r--r--engines/kyra/saveload.cpp2
-rw-r--r--engines/kyra/scene_lol.cpp2
-rw-r--r--engines/kyra/scene_mr.cpp2
-rw-r--r--engines/kyra/screen.cpp8
-rw-r--r--engines/kyra/screen_lol.cpp62
-rw-r--r--engines/kyra/screen_lol.h1
-rw-r--r--engines/kyra/screen_v2.cpp69
-rw-r--r--engines/kyra/screen_v2.h2
-rw-r--r--engines/kyra/script_tim.cpp4
-rw-r--r--engines/kyra/sequences_lok.cpp74
-rw-r--r--engines/kyra/sound_intern.h47
-rw-r--r--engines/kyra/sound_lok.cpp6
-rw-r--r--engines/kyra/sound_lol.cpp2
-rw-r--r--engines/kyra/sound_midi.cpp8
-rw-r--r--engines/kyra/sound_towns.cpp4254
-rw-r--r--engines/kyra/staticres.cpp864
-rw-r--r--engines/kyra/staticres_lol.cpp883
-rw-r--r--engines/kyra/text_hof.cpp3
-rw-r--r--engines/kyra/text_lok.cpp37
-rw-r--r--engines/kyra/text_mr.cpp3
-rw-r--r--engines/kyra/timer_hof.cpp3
-rw-r--r--engines/kyra/timer_lok.cpp94
-rw-r--r--engines/kyra/vqa.cpp4
-rw-r--r--engines/kyra/vqa.h3
-rw-r--r--engines/lure/debugger.cpp2
-rw-r--r--engines/lure/detection.cpp6
-rw-r--r--engines/lure/fights.cpp3
-rw-r--r--engines/lure/game.cpp5
-rw-r--r--engines/lure/game.h4
-rw-r--r--engines/lure/sound.cpp31
-rw-r--r--engines/lure/sound.h14
-rw-r--r--engines/m4/animation.cpp601
-rw-r--r--engines/m4/animation.h115
-rw-r--r--engines/m4/assets.cpp116
-rw-r--r--engines/m4/assets.h34
-rw-r--r--engines/m4/console.cpp10
-rw-r--r--engines/m4/converse.cpp4
-rw-r--r--engines/m4/detection.cpp6
-rw-r--r--engines/m4/events.cpp7
-rw-r--r--engines/m4/events.h1
-rw-r--r--engines/m4/font.cpp60
-rw-r--r--engines/m4/font.h48
-rw-r--r--engines/m4/globals.cpp33
-rw-r--r--engines/m4/globals.h18
-rw-r--r--engines/m4/graphics.cpp324
-rw-r--r--engines/m4/graphics.h57
-rw-r--r--engines/m4/hotspot.cpp7
-rw-r--r--engines/m4/hotspot.h4
-rw-r--r--engines/m4/m4.cpp48
-rw-r--r--engines/m4/m4.h12
-rw-r--r--engines/m4/m4_scene.cpp14
-rw-r--r--engines/m4/m4_scene.h2
-rw-r--r--engines/m4/mads_anim.cpp210
-rw-r--r--engines/m4/mads_anim.h42
-rw-r--r--engines/m4/mads_logic.cpp334
-rw-r--r--engines/m4/mads_logic.h19
-rw-r--r--engines/m4/mads_menus.cpp23
-rw-r--r--engines/m4/mads_menus.h2
-rw-r--r--engines/m4/mads_player.cpp792
-rw-r--r--engines/m4/mads_player.h116
-rw-r--r--engines/m4/mads_scene.cpp1180
-rw-r--r--engines/m4/mads_scene.h169
-rw-r--r--engines/m4/mads_views.cpp1521
-rw-r--r--engines/m4/mads_views.h306
-rw-r--r--engines/m4/midi.cpp2
-rw-r--r--engines/m4/module.mk1
-rw-r--r--engines/m4/rails.cpp2
-rw-r--r--engines/m4/rails.h2
-rw-r--r--engines/m4/scene.cpp29
-rw-r--r--engines/m4/scene.h10
-rw-r--r--engines/m4/sound.cpp38
-rw-r--r--engines/m4/sound.h2
-rw-r--r--engines/m4/sprite.cpp103
-rw-r--r--engines/m4/sprite.h14
-rw-r--r--engines/m4/viewmgr.h13
-rw-r--r--engines/made/database.cpp7
-rw-r--r--engines/made/detection.cpp61
-rw-r--r--engines/made/made.cpp8
-rw-r--r--engines/made/resource.cpp7
-rw-r--r--engines/made/scriptfuncs.cpp83
-rw-r--r--engines/made/scriptfuncs.h17
-rw-r--r--engines/mohawk/console.cpp43
-rw-r--r--engines/mohawk/console.h1
-rw-r--r--engines/mohawk/detection.cpp847
-rw-r--r--engines/mohawk/detection_tables.h1030
-rw-r--r--engines/mohawk/dialogs.cpp17
-rw-r--r--engines/mohawk/graphics.cpp53
-rw-r--r--engines/mohawk/graphics.h12
-rw-r--r--engines/mohawk/jpeg.cpp87
-rw-r--r--engines/mohawk/module.mk10
-rw-r--r--engines/mohawk/mohawk.cpp2
-rw-r--r--engines/mohawk/myst.cpp2
-rw-r--r--engines/mohawk/myst_pict.cpp272
-rw-r--r--engines/mohawk/myst_scripts.cpp2
-rw-r--r--engines/mohawk/resource.cpp2
-rw-r--r--engines/mohawk/riven.cpp119
-rw-r--r--engines/mohawk/riven.h20
-rw-r--r--engines/mohawk/riven_external.cpp258
-rw-r--r--engines/mohawk/riven_external.h3
-rw-r--r--engines/mohawk/riven_saveload.cpp67
-rw-r--r--engines/mohawk/riven_saveload.h3
-rw-r--r--engines/mohawk/riven_scripts.cpp86
-rw-r--r--engines/mohawk/riven_scripts.h33
-rw-r--r--engines/mohawk/riven_vars.cpp2
-rw-r--r--engines/mohawk/sound.cpp15
-rw-r--r--engines/mohawk/video.cpp415
-rw-r--r--engines/mohawk/video.h114
-rw-r--r--engines/parallaction/callables_ns.cpp17
-rw-r--r--engines/parallaction/detection.cpp6
-rw-r--r--engines/parallaction/exec.cpp4
-rw-r--r--engines/parallaction/exec_br.cpp7
-rw-r--r--engines/parallaction/gfxbase.cpp60
-rw-r--r--engines/parallaction/graphics.cpp8
-rw-r--r--engines/parallaction/gui_br.cpp30
-rw-r--r--engines/parallaction/input.cpp11
-rw-r--r--engines/parallaction/parallaction.h3
-rw-r--r--engines/parallaction/parallaction_br.cpp31
-rw-r--r--engines/parallaction/parallaction_ns.cpp5
-rw-r--r--engines/parallaction/parser.h1
-rw-r--r--engines/parallaction/parser_br.cpp13
-rw-r--r--engines/parallaction/walk.cpp35
-rw-r--r--engines/queen/music.cpp9
-rw-r--r--engines/queen/queen.cpp6
-rw-r--r--engines/queen/resource.cpp11
-rw-r--r--engines/saga/actor.cpp4
-rw-r--r--engines/saga/console.cpp3
-rw-r--r--engines/saga/detection.cpp6
-rw-r--r--engines/saga/events.cpp1
-rw-r--r--engines/saga/font.h2
-rw-r--r--engines/saga/interface.cpp9
-rw-r--r--engines/saga/music.cpp275
-rw-r--r--engines/saga/music.h52
-rw-r--r--engines/saga/puzzle.cpp10
-rw-r--r--engines/saga/render.cpp6
-rw-r--r--engines/saga/saga.cpp21
-rw-r--r--engines/saga/saga.h1
-rw-r--r--engines/saga/scene.cpp12
-rw-r--r--engines/saga/script.cpp2
-rw-r--r--engines/saga/script.h3
-rw-r--r--engines/saga/sfuncs.cpp19
-rw-r--r--engines/saga/sfuncs_ihnm.cpp12
-rw-r--r--engines/saga/sndres.cpp2
-rw-r--r--engines/saga/sound.cpp13
-rw-r--r--engines/saga/sound.h3
-rw-r--r--engines/saga/sthread.cpp8
-rw-r--r--engines/sci/console.cpp1272
-rw-r--r--engines/sci/console.h22
-rw-r--r--engines/sci/debug.h7
-rw-r--r--engines/sci/decompressor.cpp16
-rw-r--r--engines/sci/detection.cpp370
-rw-r--r--engines/sci/detection_tables.h1225
-rw-r--r--engines/sci/engine/features.cpp254
-rw-r--r--engines/sci/engine/features.h16
-rw-r--r--engines/sci/engine/game.cpp320
-rw-r--r--engines/sci/engine/gc.cpp163
-rw-r--r--engines/sci/engine/gc.h15
-rw-r--r--engines/sci/engine/kernel.cpp1254
-rw-r--r--engines/sci/engine/kernel.h256
-rw-r--r--engines/sci/engine/kernel32.cpp790
-rw-r--r--engines/sci/engine/kernel_tables.h1031
-rw-r--r--engines/sci/engine/kevent.cpp99
-rw-r--r--engines/sci/engine/kfile.cpp992
-rw-r--r--engines/sci/engine/kgraphics.cpp1019
-rw-r--r--engines/sci/engine/klists.cpp459
-rw-r--r--engines/sci/engine/kmath.cpp64
-rw-r--r--engines/sci/engine/kmenu.cpp8
-rw-r--r--engines/sci/engine/kmisc.cpp197
-rw-r--r--engines/sci/engine/kmovement.cpp276
-rw-r--r--engines/sci/engine/kparse.cpp51
-rw-r--r--engines/sci/engine/kpathing.cpp331
-rw-r--r--engines/sci/engine/kscripts.cpp152
-rw-r--r--engines/sci/engine/ksound.cpp106
-rw-r--r--engines/sci/engine/kstring.cpp246
-rw-r--r--engines/sci/engine/kvideo.cpp306
-rw-r--r--engines/sci/engine/message.cpp20
-rw-r--r--engines/sci/engine/savegame.cpp760
-rw-r--r--engines/sci/engine/savegame.h8
-rw-r--r--engines/sci/engine/script.cpp913
-rw-r--r--engines/sci/engine/script.h364
-rw-r--r--engines/sci/engine/script_patches.cpp353
-rw-r--r--engines/sci/engine/scriptdebug.cpp285
-rw-r--r--engines/sci/engine/seg_manager.cpp480
-rw-r--r--engines/sci/engine/seg_manager.h149
-rw-r--r--engines/sci/engine/segment.cpp623
-rw-r--r--engines/sci/engine/segment.h382
-rw-r--r--engines/sci/engine/selector.cpp87
-rw-r--r--engines/sci/engine/selector.h153
-rw-r--r--engines/sci/engine/state.cpp134
-rw-r--r--engines/sci/engine/state.h92
-rw-r--r--engines/sci/engine/static_selectors.cpp154
-rw-r--r--engines/sci/engine/vm.cpp1423
-rw-r--r--engines/sci/engine/vm.h449
-rw-r--r--engines/sci/engine/vm_types.h1
-rw-r--r--engines/sci/engine/workarounds.cpp457
-rw-r--r--engines/sci/engine/workarounds.h109
-rw-r--r--engines/sci/event.cpp85
-rw-r--r--engines/sci/event.h58
-rw-r--r--engines/sci/graphics/animate.cpp582
-rw-r--r--engines/sci/graphics/animate.h23
-rw-r--r--engines/sci/graphics/cache.cpp6
-rw-r--r--engines/sci/graphics/cache.h2
-rw-r--r--engines/sci/graphics/compare.cpp155
-rw-r--r--engines/sci/graphics/compare.h6
-rw-r--r--engines/sci/graphics/controls.cpp25
-rw-r--r--engines/sci/graphics/coordadjuster.cpp43
-rw-r--r--engines/sci/graphics/coordadjuster.h11
-rw-r--r--engines/sci/graphics/cursor.cpp109
-rw-r--r--engines/sci/graphics/cursor.h10
-rw-r--r--engines/sci/graphics/font.cpp7
-rw-r--r--engines/sci/graphics/font.h16
-rw-r--r--engines/sci/graphics/fontsjis.h6
-rw-r--r--engines/sci/graphics/frameout.cpp528
-rw-r--r--engines/sci/graphics/frameout.h39
-rw-r--r--engines/sci/graphics/gui.cpp144
-rw-r--r--engines/sci/graphics/gui.h93
-rw-r--r--engines/sci/graphics/gui32.cpp83
-rw-r--r--engines/sci/graphics/helpers.h4
-rw-r--r--engines/sci/graphics/maciconbar.cpp92
-rw-r--r--engines/sci/graphics/maciconbar.h (renamed from engines/sci/graphics/gui32.h)47
-rw-r--r--engines/sci/graphics/menu.cpp121
-rw-r--r--engines/sci/graphics/menu.h11
-rw-r--r--engines/sci/graphics/paint.cpp4
-rw-r--r--engines/sci/graphics/paint.h6
-rw-r--r--engines/sci/graphics/paint16.cpp149
-rw-r--r--engines/sci/graphics/paint16.h31
-rw-r--r--engines/sci/graphics/paint32.cpp7
-rw-r--r--engines/sci/graphics/paint32.h3
-rw-r--r--engines/sci/graphics/palette.cpp503
-rw-r--r--engines/sci/graphics/palette.h56
-rw-r--r--engines/sci/graphics/picture.cpp273
-rw-r--r--engines/sci/graphics/picture.h15
-rw-r--r--engines/sci/graphics/portrait.cpp12
-rw-r--r--engines/sci/graphics/portrait.h5
-rw-r--r--engines/sci/graphics/ports.cpp186
-rw-r--r--engines/sci/graphics/ports.h21
-rw-r--r--engines/sci/graphics/robot.cpp42
-rw-r--r--engines/sci/graphics/robot.h7
-rw-r--r--engines/sci/graphics/screen.cpp164
-rw-r--r--engines/sci/graphics/screen.h52
-rw-r--r--engines/sci/graphics/text16.cpp165
-rw-r--r--engines/sci/graphics/text16.h8
-rw-r--r--engines/sci/graphics/transitions.cpp144
-rw-r--r--engines/sci/graphics/transitions.h4
-rw-r--r--engines/sci/graphics/view.cpp483
-rw-r--r--engines/sci/graphics/view.h37
-rw-r--r--engines/sci/module.mk17
-rw-r--r--engines/sci/parser/grammar.cpp49
-rw-r--r--engines/sci/parser/said.cpp2830
-rw-r--r--engines/sci/parser/said.y838
-rw-r--r--engines/sci/parser/vocabulary.cpp382
-rw-r--r--engines/sci/parser/vocabulary.h39
-rw-r--r--engines/sci/resource.cpp1839
-rw-r--r--engines/sci/resource.h361
-rw-r--r--engines/sci/resource_audio.cpp783
-rw-r--r--engines/sci/resource_intern.h219
-rw-r--r--engines/sci/sci.cpp475
-rw-r--r--engines/sci/sci.h187
-rw-r--r--engines/sci/sound/audio.cpp112
-rw-r--r--engines/sci/sound/audio.h5
-rw-r--r--engines/sci/sound/drivers/adlib.cpp14
-rw-r--r--engines/sci/sound/drivers/amigamac.cpp (renamed from engines/sci/sound/drivers/amiga.cpp)562
-rw-r--r--engines/sci/sound/drivers/fb01.cpp4
-rw-r--r--engines/sci/sound/drivers/midi.cpp25
-rw-r--r--engines/sci/sound/drivers/mididriver.h3
-rw-r--r--engines/sci/sound/midiparser_sci.cpp654
-rw-r--r--engines/sci/sound/midiparser_sci.h31
-rw-r--r--engines/sci/sound/music.cpp278
-rw-r--r--engines/sci/sound/music.h53
-rw-r--r--engines/sci/sound/soundcmd.cpp1070
-rw-r--r--engines/sci/sound/soundcmd.h98
-rw-r--r--engines/sci/video/seq_decoder.cpp5
-rw-r--r--engines/sci/video/seq_decoder.h2
-rw-r--r--engines/sci/video/vmd_decoder.cpp119
-rw-r--r--engines/sci/video/vmd_decoder.h89
-rw-r--r--engines/scumm/charset.cpp7
-rw-r--r--engines/scumm/debugger.cpp12
-rw-r--r--engines/scumm/detection.cpp60
-rw-r--r--engines/scumm/detection_tables.h29
-rw-r--r--engines/scumm/dialogs.cpp7
-rw-r--r--engines/scumm/gfx.cpp4
-rw-r--r--engines/scumm/gfx.h1
-rw-r--r--engines/scumm/imuse/instrument.cpp6
-rw-r--r--engines/scumm/player_nes.cpp3
-rw-r--r--engines/scumm/saveload.cpp8
-rw-r--r--engines/scumm/script_v5.cpp11
-rw-r--r--engines/scumm/scumm-md5.h50
-rw-r--r--engines/scumm/scumm.cpp43
-rw-r--r--engines/scumm/sound.cpp2
-rw-r--r--engines/scumm/string.cpp4
-rw-r--r--engines/scumm/verbs.h2
-rw-r--r--engines/sky/sky.cpp13
-rw-r--r--engines/sword1/control.cpp3
-rw-r--r--engines/sword1/memman.h4
-rw-r--r--engines/sword1/music.cpp2
-rw-r--r--engines/sword2/anims.cpp8
-rw-r--r--engines/sword2/music.cpp6
-rw-r--r--engines/sword2/resman.h4
-rw-r--r--engines/sword2/sword2.cpp22
-rw-r--r--engines/teenagent/detection.cpp4
-rw-r--r--engines/teenagent/module.mk24
-rw-r--r--engines/teenagent/teenagent.cpp4
-rw-r--r--engines/tinsel/actors.cpp6
-rw-r--r--engines/tinsel/bmv.cpp12
-rw-r--r--engines/tinsel/cliprect.cpp12
-rw-r--r--engines/tinsel/detection.cpp550
-rw-r--r--engines/tinsel/detection_tables.h567
-rw-r--r--engines/tinsel/graphics.cpp23
-rw-r--r--engines/tinsel/handle.cpp16
-rw-r--r--engines/tinsel/object.cpp6
-rw-r--r--engines/tinsel/pcode.cpp7
-rw-r--r--engines/tinsel/saveload.cpp3
-rw-r--r--engines/tinsel/savescn.cpp6
-rw-r--r--engines/tinsel/strres.cpp14
-rw-r--r--engines/tinsel/tinlib.cpp2
-rw-r--r--engines/tinsel/tinsel.cpp11
-rw-r--r--engines/tinsel/tinsel.h6
-rw-r--r--engines/touche/detection.cpp11
-rw-r--r--engines/touche/midi.cpp6
-rw-r--r--engines/touche/touche.cpp6
-rw-r--r--engines/tucker/detection.cpp4
-rw-r--r--engines/tucker/sequences.cpp67
-rw-r--r--engines/tucker/tucker.h5
522 files changed, 43681 insertions, 37607 deletions
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index b149b43ad7..f4af4a8500 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -208,6 +208,10 @@ static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *
desc["extra"] = realDesc->extra;
desc.setGUIOptions(realDesc->guioptions | params.guioptions);
+ desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language));
+
+ if (realDesc->flags & ADGF_ADDENGLISH)
+ desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
}
GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
@@ -305,7 +309,12 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
// If the GUI options were updated, we catch this here and update them in the users config
// file transparently.
- Common::updateGameGUIOptions(agdDesc->guioptions | params.guioptions);
+ Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc->language);
+ if (agdDesc->flags & ADGF_ADDENGLISH)
+ lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY);
+
+ Common::updateGameGUIOptions(agdDesc->guioptions | params.guioptions, lang);
+
debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str());
if (!createInstance(syst, engine, agdDesc))
@@ -340,24 +349,37 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz
static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams &params);
-static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &params, Common::Language language, Common::Platform platform, const Common::String &extra) {
- FileMap allFiles;
- SizeMD5Map filesSizeMD5;
-
- const ADGameFileDescription *fileDesc;
- const ADGameDescription *g;
- const byte *descPtr;
+static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char **directoryGlobs) {
+ if (depth <= 0)
+ return;
if (fslist.empty())
- return ADGameDescList();
- Common::FSNode parent = fslist.begin()->getParent();
- debug(3, "Starting detection in dir '%s'", parent.getPath().c_str());
+ return;
// First we compose a hashmap of all files in fslist.
// Includes nifty stuff like removing trailing dots and ignoring case.
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
- if (file->isDirectory())
- continue;
+ if (file->isDirectory()) {
+ Common::FSList files;
+
+ if (!directoryGlobs)
+ continue;
+
+ bool matched = false;
+ for (const char *glob = *directoryGlobs; *glob; glob++)
+ if (file->getName().matchString(glob, true)) {
+ matched = true;
+ break;
+ }
+
+ if (!matched)
+ continue;
+
+ if (!file->getChildren(files, Common::FSNode::kListAll))
+ continue;
+
+ composeFileHashMap(files, allFiles, depth - 1, directoryGlobs);
+ }
Common::String tstr = file->getName();
@@ -367,6 +389,24 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
allFiles[tstr] = *file; // Record the presence of this file
}
+}
+
+static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &params, Common::Language language, Common::Platform platform, const Common::String &extra) {
+ FileMap allFiles;
+ SizeMD5Map filesSizeMD5;
+
+ const ADGameFileDescription *fileDesc;
+ const ADGameDescription *g;
+ const byte *descPtr;
+
+ if (fslist.empty())
+ return ADGameDescList();
+ Common::FSNode parent = fslist.begin()->getParent();
+ debug(3, "Starting detection in dir '%s'", parent.getPath().c_str());
+
+ // First we compose a hashmap of all files in fslist.
+ // Includes nifty stuff like removing trailing dots and ignoring case.
+ composeFileHashMap(fslist, allFiles, (params.depth == 0 ? 1 : params.depth), params.directoryGlobs);
// Check which files are included in some ADGameDescription *and* present
// in fslist. Compute MD5s and file sizes for these files.
@@ -423,7 +463,8 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
// Do not even bother to look at entries which do not have matching
// language and platform (if specified).
- if ((language != Common::UNK_LANG && g->language != Common::UNK_LANG && g->language != language) ||
+ if ((language != Common::UNK_LANG && g->language != Common::UNK_LANG && g->language != language
+ && !(language == Common::EN_ANY && (g->flags & ADGF_ADDENGLISH))) ||
(platform != Common::kPlatformUnknown && g->platform != Common::kPlatformUnknown && g->platform != platform)) {
continue;
}
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 370d958ce6..1e59df04bf 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -38,11 +38,14 @@ struct ADGameFileDescription {
int32 fileSize; // Optional. Set to -1 to ignore.
};
-#define AD_ENTRY1(f, x) {{ f, 0, x, -1}, {NULL, 0, NULL, 0}}
-#define AD_ENTRY1s(f, x, s) {{ f, 0, x, s}, {NULL, 0, NULL, 0}}
+#define AD_LISTEND {NULL, 0, NULL, 0}
+
+#define AD_ENTRY1(f, x) {{ f, 0, x, -1}, AD_LISTEND}
+#define AD_ENTRY1s(f, x, s) {{ f, 0, x, s}, AD_LISTEND}
enum ADGameFlags {
ADGF_NO_FLAGS = 0,
+ ADGF_ADDENGLISH = (1 << 24), // always add English as language option
ADGF_MACRESFORK = (1 << 25), // the md5 for this entry will be calculated from the resource fork
ADGF_USEEXTRAASTITLE = (1 << 26), // Extra field value will be used as main game title, not gameid
ADGF_KEEPMATCH = (1 << 27), // this entry is kept even when there are matched entries with more files
@@ -190,6 +193,21 @@ struct ADParams {
* enum for the list.
*/
uint32 guioptions;
+
+ /**
+ * Maximum depth of directories to look up
+ * If set to 0, the depth is 1 level
+ */
+ uint32 depth;
+
+ /**
+ * Case-insensitive list of directory globs which could be used for
+ * going deeper int directory structure.
+ * @see String::matchString() method for format description.
+ *
+ * @note Last item must be 0
+ */
+ const char **directoryGlobs;
};
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index c2c6d10bfe..e83ef4ead9 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -272,20 +272,18 @@ void AgiEngine::processEvents() {
}
void AgiEngine::pollTimer() {
- static uint32 m = 0;
uint32 dm;
- if (_tickTimer < m)
- m = 0;
+ if (_tickTimer < _lastTickTimer)
+ _lastTickTimer = 0;
- while ((dm = _tickTimer - m) < 5) {
+ while ((dm = _tickTimer - _lastTickTimer) < 5) {
processEvents();
- if (_console->isAttached())
- _console->onFrame();
+ _console->onFrame();
_system->delayMillis(10);
_system->updateScreen();
}
- m = _tickTimer;
+ _lastTickTimer = _tickTimer;
}
void AgiEngine::agiTimerFunctionLow(void *refCon) {
@@ -345,7 +343,7 @@ int AgiEngine::agiInit() {
// clear view table
for (i = 0; i < MAX_VIEWTABLE; i++)
- memset(&_game.viewTable[i], 0, sizeof(VtEntry));
+ memset(&_game.viewTable[i], 0, sizeof(struct VtEntry));
initWords();
@@ -506,13 +504,6 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
- const GameSettings *g;
-
- const char *gameid = ConfMan.get("gameid").c_str();
- for (g = agiSettings; g->gameid; ++g)
- if (!scumm_stricmp(g->gameid, gameid))
- _gameId = g->id;
-
parseFeatures();
_rnd = new Common::RandomSource();
@@ -543,6 +534,7 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_allowSynthetic = false;
_tickTimer = 0;
+ _lastTickTimer = 0;
_intobj = NULL;
@@ -556,7 +548,7 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_restartGame = false;
- _oldMode = -1;
+ _oldMode = INPUT_NONE;
_predictiveDialogRunning = false;
_predictiveDictText = NULL;
@@ -569,6 +561,10 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_game.lastController = 0;
for (int i = 0; i < MAX_DIRS; i++)
_game.controllerOccured[i] = false;
+
+ setupOpcodes();
+ _curLogic = NULL;
+ _timerHack = 0;
}
void AgiEngine::initialize() {
@@ -583,13 +579,19 @@ void AgiEngine::initialize() {
} else if (getPlatform() == Common::kPlatformCoCo3) {
_soundemu = SOUND_EMU_COCO3;
} else {
- switch (MidiDriver::detectMusicDriver(MDT_PCSPK)) {
- case MD_PCSPK:
+ switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK|MDT_ADLIB|MDT_PCJR|MDT_MIDI))) {
+ case MT_PCSPK:
_soundemu = SOUND_EMU_PC;
break;
- default:
+ case MT_PCJR:
+ _soundemu = SOUND_EMU_PCJR;
+ break;
+ case MT_ADLIB:
_soundemu = SOUND_EMU_NONE;
break;
+ default:
+ _soundemu = SOUND_EMU_MIDI;
+ break;
}
}
@@ -607,6 +609,8 @@ void AgiEngine::initialize() {
_renderMode = Common::kRenderEGA;
break;
}
+ } else {
+ _renderMode = Common::kRenderDefault;
}
_buttonStyle = AgiButtonStyle(_renderMode);
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index fb9e204101..507e7f7a11 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -37,6 +37,14 @@
#include "gui/debugger.h"
+// AGI resources
+#include "agi/console.h"
+#include "agi/view.h"
+#include "agi/picture.h"
+#include "agi/logic.h"
+#include "agi/sound.h"
+
+
namespace Common { class RandomSource; }
/**
@@ -110,23 +118,13 @@ enum AgiGameID {
GID_SQ2,
GID_XMASCARD,
GID_FANMADE,
- GID_GETOUTTASQ,
+ GID_GETOUTTASQ, // Fanmade
+ GID_SQ0, // Fanmade
GID_MICKEY, // PreAGI
GID_WINNIE, // PreAGI
- GID_TROLL // PreAGI
+ GID_TROLL // PreAGI
};
-} // End of namespace Agi
-
-// AGI resources
-#include "agi/console.h"
-#include "agi/view.h"
-#include "agi/picture.h"
-#include "agi/logic.h"
-#include "agi/sound.h"
-
-namespace Agi {
-
enum AgiGameType {
GType_PreAGI = 0,
GType_V2 = 1,
@@ -151,7 +149,8 @@ enum AgiGameFeatures {
GF_MENUS = (1 << 7),
GF_ESCPAUSE = (1 << 8),
GF_OLDAMIGAV20 = (1 << 9),
- GF_CLIPCOORDS = (1 << 10)
+ GF_CLIPCOORDS = (1 << 10),
+ GF_2GSOLDSOUND = (1 << 11)
};
struct AGIGameDescription;
@@ -316,6 +315,12 @@ enum AgiComputerType {
kAgiComputerAmigaOld = 20 // Older Amiga AGI interpreters' value (Seldom used)
};
+enum AgiSoundType {
+ kAgiSoundPC = 1,
+ kAgiSoundTandy = 3, // Tandy (This value is also used by the Amiga AGI and Apple IIGS AGI)
+ kAgiSound2GSOld = 8 // Apple IIGS's Gold Rush! (Version 1.0M 1989-02-28 (CE), AGI 3.003) uses value 8
+};
+
/**
* AGI flags
*/
@@ -497,10 +502,32 @@ struct ScriptPos {
int curIP;
};
-#define EGO_VIEW_TABLE 0
-#define HORIZON 36
-#define _WIDTH 160
-#define _HEIGHT 168
+enum {
+ EGO_VIEW_TABLE = 0,
+ HORIZON = 36,
+ _WIDTH = 160,
+ _HEIGHT = 168
+};
+
+enum InputMode {
+ INPUT_NORMAL = 0x01,
+ INPUT_GETSTRING = 0x02,
+ INPUT_MENU = 0x03,
+ INPUT_NONE = 0x04
+};
+
+enum State {
+ STATE_INIT = 0x00,
+ STATE_LOADED = 0x01,
+ STATE_RUNNING = 0x02
+};
+
+enum {
+ SBUF16_OFFSET = 0,
+ SBUF256_OFFSET = ((_WIDTH) * (_HEIGHT)),
+ FROM_SBUF16_TO_SBUF256_OFFSET = ((SBUF256_OFFSET) - (SBUF16_OFFSET)),
+ FROM_SBUF256_TO_SBUF16_OFFSET = ((SBUF16_OFFSET) - (SBUF256_OFFSET))
+};
/**
* AGI game structure.
@@ -508,10 +535,7 @@ struct ScriptPos {
* by the interpreter.
*/
struct AgiGame {
-#define STATE_INIT 0x00
-#define STATE_LOADED 0x01
-#define STATE_RUNNING 0x02
- int state; /**< state of the interpreter */
+ State state; /**< state of the interpreter */
// TODO: Check whether adjMouseX and adjMouseY must be saved and loaded when using savegames.
// If they must be then loading and saving is partially broken at the moment.
@@ -535,12 +559,9 @@ struct AgiGame {
uint8 inputBuffer[40]; /**< buffer for user input */
uint8 echoBuffer[40]; /**< buffer for echo.line */
int keypress;
-#define INPUT_NORMAL 0x01
-#define INPUT_GETSTRING 0x02
-#define INPUT_MENU 0x03
-#define INPUT_NONE 0x04
- int inputMode; /**< keyboard input mode */
- int inputEnabled; /**< keyboard input enabled */
+
+ InputMode inputMode; /**< keyboard input mode */
+ bool inputEnabled; /**< keyboard input enabled */
int lognum; /**< current logic number */
Common::Array<ScriptPos> execStack;
@@ -568,10 +589,7 @@ struct AgiGame {
char cursorChar;
unsigned int colorFg;
unsigned int colorBg;
-#define SBUF16_OFFSET 0
-#define SBUF256_OFFSET ((_WIDTH) * (_HEIGHT))
-#define FROM_SBUF16_TO_SBUF256_OFFSET ((SBUF256_OFFSET) - (SBUF16_OFFSET))
-#define FROM_SBUF256_TO_SBUF16_OFFSET ((SBUF16_OFFSET) - (SBUF256_OFFSET))
+
uint8 *sbufOrig; /**< Pointer to the 160x336 AGI screen buffer that contains vertically two 160x168 screens (16 color and 256 color). */
uint8 *sbuf16c; /**< 160x168 16 color (+control line & priority information) AGI screen buffer. Points at sbufOrig + SBUF16_OFFSET. */
uint8 *sbuf256c; /**< 160x168 256 color AGI screen buffer (For AGI256 and AGI256-2 support). Points at sbufOrig + SBUF256_OFFSET. */
@@ -774,8 +792,6 @@ public:
};
class AgiEngine : public AgiBase {
- int _gameId;
-
protected:
// Engine APIs
virtual Common::Error go();
@@ -788,9 +804,6 @@ protected:
public:
AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc);
virtual ~AgiEngine();
- int getGameId() {
- return _gameId;
- }
Common::Error loadGameState(int slot);
Common::Error saveGameState(int slot, const char *desc);
@@ -798,6 +811,7 @@ public:
private:
uint32 _tickTimer;
+ uint32 _lastTickTimer;
int _keyQueue[KEY_QUEUE_SIZE];
int _keyQueueStart;
@@ -829,7 +843,7 @@ public:
int loadGameSimple();
uint8 *_intobj;
- int _oldMode;
+ InputMode _oldMode;
bool _restartGame;
Menu* _menu;
@@ -871,7 +885,7 @@ public:
static void agiTimerFunctionLow(void *refCon);
void initPriTable();
- void newInputMode(int);
+ void newInputMode(InputMode mode);
void oldInputMode();
int getvar(int);
@@ -920,6 +934,17 @@ public:
int testIfCode(int);
void executeAgiCommand(uint8, uint8 *);
+private:
+ // Some submethods of testIfCode
+ uint8 testObjRight(uint8, uint8, uint8, uint8, uint8);
+ uint8 testObjCentre(uint8, uint8, uint8, uint8, uint8);
+ uint8 testObjInBox(uint8, uint8, uint8, uint8, uint8);
+ uint8 testPosn(uint8, uint8, uint8, uint8, uint8);
+ uint8 testSaid(uint8, uint8 *);
+ uint8 testController(uint8);
+ uint8 testKeypressed();
+ uint8 testCompareStrings(uint8, uint8);
+
// View
private:
@@ -1024,6 +1049,198 @@ private:
bool _predictiveDialogRunning;
public:
char _predictiveResult[40];
+
+private:
+ typedef void (AgiEngine::*AgiCommand)(uint8 *);
+
+ AgiCommand _agiCommands[183];
+ AgiLogic *_curLogic;
+ int _timerHack; // Workaround for timer loop in MH1 logic 153
+
+ void setupOpcodes();
+
+ void cmd_increment(uint8 *p);
+ void cmd_decrement(uint8 *p);
+ void cmd_assignn(uint8 *p);
+ void cmd_assignv(uint8 *p);
+ void cmd_addn(uint8 *p);
+ void cmd_addv(uint8 *p);
+ void cmd_subn(uint8 *p);
+ void cmd_subv(uint8 *p); // 0x08
+ void cmd_lindirectv(uint8 *p);
+ void cmd_rindirect(uint8 *p);
+ void cmd_lindirectn(uint8 *p);
+ void cmd_set(uint8 *p);
+ void cmd_reset(uint8 *p);
+ void cmd_toggle(uint8 *p);
+ void cmd_set_v(uint8 *p);
+ void cmd_reset_v(uint8 *p); // 0x10
+ void cmd_toggle_v(uint8 *p);
+ void cmd_new_room(uint8 *p);
+ void cmd_new_room_f(uint8 *p);
+ void cmd_load_logic(uint8 *p);
+ void cmd_load_logic_f(uint8 *p);
+ void cmd_call(uint8 *p);
+ void cmd_call_f(uint8 *p);
+ void cmd_load_pic(uint8 *p); // 0x18
+ void cmd_draw_pic(uint8 *p);
+ void cmd_show_pic(uint8 *p);
+ void cmd_discard_pic(uint8 *p);
+ void cmd_overlay_pic(uint8 *p);
+ void cmd_show_pri_screen(uint8 *p);
+ void cmd_load_view(uint8 *p);
+ void cmd_load_view_f(uint8 *p);
+ void cmd_discard_view(uint8 *p); // 0x20
+ void cmd_animate_obj(uint8 *p);
+ void cmd_unanimate_all(uint8 *p);
+ void cmd_draw(uint8 *p);
+ void cmd_erase(uint8 *p);
+ void cmd_position(uint8 *p);
+ void cmd_position_f(uint8 *p);
+ void cmd_get_posn(uint8 *p);
+ void cmd_reposition(uint8 *p); // 0x28
+ void cmd_set_view(uint8 *p);
+ void cmd_set_view_f(uint8 *p);
+ void cmd_set_loop(uint8 *p);
+ void cmd_set_loop_f(uint8 *p);
+ void cmd_fix_loop(uint8 *p);
+ void cmd_release_loop(uint8 *p);
+ void cmd_set_cel(uint8 *p);
+ void cmd_set_cel_f(uint8 *p); // 0x30
+ void cmd_last_cel(uint8 *p);
+ void cmd_current_cel(uint8 *p);
+ void cmd_current_loop(uint8 *p);
+ void cmd_current_view(uint8 *p);
+ void cmd_number_of_loops(uint8 *p);
+ void cmd_set_priority(uint8 *p);
+ void cmd_set_priority_f(uint8 *p);
+ void cmd_release_priority(uint8 *p); // 0x38
+ void cmd_get_priority(uint8 *p);
+ void cmd_stop_update(uint8 *p);
+ void cmd_start_update(uint8 *p);
+ void cmd_force_update(uint8 *p);
+ void cmd_ignore_horizon(uint8 *p);
+ void cmd_observe_horizon(uint8 *p);
+ void cmd_set_horizon(uint8 *p);
+ void cmd_object_on_water(uint8 *p); // 0x40
+ void cmd_object_on_land(uint8 *p);
+ void cmd_object_on_anything(uint8 *p);
+ void cmd_ignore_objs(uint8 *p);
+ void cmd_observe_objs(uint8 *p);
+ void cmd_distance(uint8 *p);
+ void cmd_stop_cycling(uint8 *p);
+ void cmd_start_cycling(uint8 *p);
+ void cmd_normal_cycle(uint8 *p); // 0x48
+ void cmd_end_of_loop(uint8 *p);
+ void cmd_reverse_cycle(uint8 *p);
+ void cmd_reverse_loop(uint8 *p);
+ void cmd_cycle_time(uint8 *p);
+ void cmd_stop_motion(uint8 *p);
+ void cmd_start_motion(uint8 *p);
+ void cmd_step_size(uint8 *p);
+ void cmd_step_time(uint8 *p); // 0x50
+ void cmd_move_obj(uint8 *p);
+ void cmd_move_obj_f(uint8 *p);
+ void cmd_follow_ego(uint8 *p);
+ void cmd_wander(uint8 *p);
+ void cmd_normal_motion(uint8 *p);
+ void cmd_set_dir(uint8 *p);
+ void cmd_get_dir(uint8 *p);
+ void cmd_ignore_blocks(uint8 *p); // 0x58
+ void cmd_observe_blocks(uint8 *p);
+ void cmd_block(uint8 *p);
+ void cmd_unblock(uint8 *p);
+ void cmd_get(uint8 *p);
+ void cmd_get_f(uint8 *p);
+ void cmd_drop(uint8 *p);
+ void cmd_put(uint8 *p);
+ void cmd_put_f(uint8 *p); // 0x60
+ void cmd_get_room_f(uint8 *p);
+ void cmd_load_sound(uint8 *p);
+ void cmd_sound(uint8 *p);
+ void cmd_stop_sound(uint8 *p);
+ void cmd_print(uint8 *p);
+ void cmd_print_f(uint8 *p);
+ void cmd_display(uint8 *p);
+ void cmd_display_f(uint8 *p); // 0x68
+ void cmd_clear_lines(uint8 *p);
+ void cmd_text_screen(uint8 *p);
+ void cmd_graphics(uint8 *p);
+ void cmd_set_cursor_char(uint8 *p);
+ void cmd_set_text_attribute(uint8 *p);
+ void cmd_shake_screen(uint8 *p);
+ void cmd_configure_screen(uint8 *p);
+ void cmd_status_line_on(uint8 *p); // 0x70
+ void cmd_status_line_off(uint8 *p);
+ void cmd_set_string(uint8 *p);
+ void cmd_get_string(uint8 *p);
+ void cmd_word_to_string(uint8 *p);
+ void cmd_parse(uint8 *p);
+ void cmd_get_num(uint8 *p);
+ void cmd_prevent_input(uint8 *p);
+ void cmd_accept_input(uint8 *p); // 0x78
+ void cmd_set_key(uint8 *p);
+ void cmd_add_to_pic(uint8 *p);
+ void cmd_add_to_pic_f(uint8 *p);
+ void cmd_status(uint8 *p);
+ void cmd_save_game(uint8 *p);
+ void cmd_load_game(uint8 *p);
+ void cmd_init_disk(uint8 *p);
+ void cmd_restart_game(uint8 *p); // 0x80
+ void cmd_show_obj(uint8 *p);
+ void cmd_random(uint8 *p);
+ void cmd_program_control(uint8 *p);
+ void cmd_player_control(uint8 *p);
+ void cmd_obj_status_f(uint8 *p);
+ void cmd_quit(uint8 *p);
+ void cmd_show_mem(uint8 *p);
+ void cmd_pause(uint8 *p); // 0x88
+ void cmd_echo_line(uint8 *p);
+ void cmd_cancel_line(uint8 *p);
+ void cmd_init_joy(uint8 *p);
+ void cmd_toggle_monitor(uint8 *p);
+ void cmd_version(uint8 *p);
+ void cmd_script_size(uint8 *p);
+ void cmd_set_game_id(uint8 *p);
+ void cmd_log(uint8 *p); // 0x90
+ void cmd_set_scan_start(uint8 *p);
+ void cmd_reset_scan_start(uint8 *p);
+ void cmd_reposition_to(uint8 *p);
+ void cmd_reposition_to_f(uint8 *p);
+ void cmd_trace_on(uint8 *p);
+ void cmd_trace_info(uint8 *p);
+ void cmd_print_at(uint8 *p);
+ void cmd_print_at_v(uint8 *p); // 0x98
+ //void cmd_discard_view(uint8 *p); // Opcode repeated from 0x20 ?
+ void cmd_clear_text_rect(uint8 *p);
+ void cmd_set_upper_left(uint8 *p);
+ void cmd_set_menu(uint8 *p);
+ void cmd_set_menu_item(uint8 *p);
+ void cmd_submit_menu(uint8 *p);
+ void cmd_enable_item(uint8 *p);
+ void cmd_disable_item(uint8 *p); // 0xa0
+ void cmd_menu_input(uint8 *p);
+ void cmd_show_obj_v(uint8 *p);
+ void cmd_open_dialogue(uint8 *p);
+ void cmd_close_dialogue(uint8 *p);
+ void cmd_mul_n(uint8 *p);
+ void cmd_mul_v(uint8 *p);
+ void cmd_div_n(uint8 *p);
+ void cmd_div_v(uint8 *p); // 0xa8
+ void cmd_close_window(uint8 *p);
+ void cmd_set_simple(uint8 *p);
+ void cmd_push_script(uint8 *p);
+ void cmd_pop_script(uint8 *p);
+ void cmd_hold_key(uint8 *p);
+ void cmd_set_pri_base(uint8 *p);
+ void cmd_discard_sound(uint8 *p);
+ void cmd_hide_mouse(uint8 *p); // 0xb0
+ void cmd_allow_menu(uint8 *p);
+ void cmd_show_mouse(uint8 *p);
+ void cmd_fence_mouse(uint8 *p);
+ void cmd_mouse_posn(uint8 *p);
+ void cmd_release_key(uint8 *p);
+ void cmd_adj_ego_move_to_x_y(uint8 *p);
};
} // End of namespace Agi
diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp
index a0621f80dd..e5942455e2 100644
--- a/engines/agi/console.cpp
+++ b/engines/agi/console.cpp
@@ -53,18 +53,9 @@ Console::Console(AgiEngine *vm) : GUI::Debugger() {
DCmd_Register("bt", WRAP_METHOD(Console, Cmd_BT));
}
-Console::~Console() {
-}
-
-void Console::preEnter() {
-}
-
-void Console::postEnter() {
-}
-
bool Console::Cmd_SetVar(int argc, const char **argv) {
if (argc != 3) {
- DebugPrintf("Usage: setvar <varnum> <value>");
+ DebugPrintf("Usage: setvar <varnum> <value>\n");
return true;
}
int p1 = (int)atoi(argv[1]);
@@ -76,7 +67,7 @@ bool Console::Cmd_SetVar(int argc, const char **argv) {
bool Console::Cmd_SetFlag(int argc, const char **argv) {
if (argc != 3) {
- DebugPrintf("Usage: setvar <varnum> <value>");
+ DebugPrintf("Usage: setvar <varnum> <value>\n");
return true;
}
int p1 = (int)atoi(argv[1]);
@@ -88,7 +79,7 @@ bool Console::Cmd_SetFlag(int argc, const char **argv) {
bool Console::Cmd_SetObj(int argc, const char **argv) {
if (argc != 3) {
- DebugPrintf("Usage: setvar <varnum> <value>");
+ DebugPrintf("Usage: setvar <varnum> <value>\n");
return true;
}
int p1 = (int)atoi(argv[1]);
@@ -99,6 +90,11 @@ bool Console::Cmd_SetObj(int argc, const char **argv) {
}
bool Console::Cmd_RunOpcode(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Usage: runopcode <name> <parameter0> ....\n");
+ return true;
+ }
+
for (int i = 0; logicNamesCmd[i].name; i++) {
if (!strcmp(argv[1], logicNamesCmd[i].name)) {
uint8 p[16];
@@ -120,6 +116,8 @@ bool Console::Cmd_RunOpcode(int argc, const char **argv) {
}
}
+ DebugPrintf("Unknown opcode\n");
+
return true;
}
@@ -243,6 +241,10 @@ bool Console::Cmd_Cont(int argc, const char **argv) {
}
bool Console::Cmd_Room(int argc, const char **argv) {
+ if (argc == 2) {
+ _vm->newRoom(strtoul(argv[1], NULL, 0));
+ }
+
DebugPrintf("Current room: %d\n", _vm->getvar(0));
return true;
diff --git a/engines/agi/console.h b/engines/agi/console.h
index e8eccbe50a..e79db42054 100644
--- a/engines/agi/console.h
+++ b/engines/agi/console.h
@@ -46,11 +46,6 @@ struct AgiDebug {
class Console : public GUI::Debugger {
public:
Console(AgiEngine *vm);
- virtual ~Console();
-
-protected:
- virtual void preEnter();
- virtual void postEnter();
private:
bool Cmd_SetVar(int argc, const char **argv);
@@ -80,10 +75,6 @@ public:
PreAGI_Console(PreAgiEngine *vm);
virtual ~PreAGI_Console() {}
-protected:
- virtual void preEnter() {}
- virtual void postEnter() {}
-
private:
PreAgiEngine *_vm;
};
@@ -94,10 +85,6 @@ public:
Mickey_Console(PreAgiEngine *vm, Mickey *mickey);
virtual ~Mickey_Console() {}
-protected:
- virtual void preEnter() {}
- virtual void postEnter() {}
-
private:
Mickey *_mickey;
@@ -112,10 +99,6 @@ public:
Winnie_Console(PreAgiEngine *vm, Winnie *winnie);
virtual ~Winnie_Console() {}
-protected:
- virtual void preEnter() {}
- virtual void postEnter() {}
-
private:
Winnie *_winnie;
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 10df40556f..b7eba22298 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -177,9 +177,12 @@ void AgiEngine::updateTimer() {
setvar(vDays, getvar(vDays) + 1);
}
-void AgiEngine::newInputMode(int i) {
+void AgiEngine::newInputMode(InputMode mode) {
+ if (mode == INPUT_MENU && !getflag(fMenusWork) && !(getFeatures() & GF_MENUS))
+ return;
+
_oldMode = _game.inputMode;
- _game.inputMode = i;
+ _game.inputMode = mode;
}
void AgiEngine::oldInputMode() {
@@ -200,7 +203,7 @@ int AgiEngine::mainCycle() {
// vars in every interpreter cycle.
//
// We run AGIMOUSE always as a side effect
- if (getFeatures() & GF_AGIMOUSE || 1) {
+ if (getFeatures() & GF_AGIMOUSE || true) {
_game.vars[28] = _mouse.x / 2;
_game.vars[29] = _mouse.y;
}
@@ -314,7 +317,8 @@ int AgiEngine::playGame() {
_game.clockEnabled = true;
_game.lineUserInput = 22;
- if (getFeatures() & GF_AGIMOUSE)
+ // We run AGIMOUSE always as a side effect
+ if (getFeatures() & GF_AGIMOUSE || true)
report("Using AGI Mouse 1.0 protocol\n");
if (getFeatures() & GF_AGIPAL)
@@ -386,28 +390,33 @@ int AgiEngine::runGame() {
_restartGame = false;
}
- // Set computer type (v20 i.e. vComputer)
+ // Set computer type (v20 i.e. vComputer) and sound type
switch (getPlatform()) {
case Common::kPlatformAtariST:
setvar(vComputer, kAgiComputerAtariST);
+ setvar(vSoundgen, kAgiSoundPC);
break;
case Common::kPlatformAmiga:
if (getFeatures() & GF_OLDAMIGAV20)
setvar(vComputer, kAgiComputerAmigaOld);
else
setvar(vComputer, kAgiComputerAmiga);
+ setvar(vSoundgen, kAgiSoundTandy);
break;
case Common::kPlatformApple2GS:
setvar(vComputer, kAgiComputerApple2GS);
+ if (getFeatures() & GF_2GSOLDSOUND)
+ setvar(vSoundgen, kAgiSound2GSOld);
+ else
+ setvar(vSoundgen, kAgiSoundTandy);
break;
case Common::kPlatformPC:
default:
setvar(vComputer, kAgiComputerPC);
+ setvar(vSoundgen, kAgiSoundPC);
break;
}
- setvar(vSoundgen, 1); // IBM PC SOUND
-
// Set monitor type (v26 i.e. vMonitor)
switch (_renderMode) {
case Common::kRenderCGA:
@@ -430,7 +439,7 @@ int AgiEngine::runGame() {
setvar(vFreePages, 180); // Set amount of free memory to realistic value
setvar(vMaxInputChars, 38);
_game.inputMode = INPUT_NONE;
- _game.inputEnabled = 0;
+ _game.inputEnabled = false;
_game.hasPrompt = 0;
_game.state = STATE_RUNNING;
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index e72647d5e2..d1bed5d716 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -125,841 +125,7 @@ static const PlainGameDescriptor agiGames[] = {
{0, 0}
};
-
-namespace Agi {
-
-using Common::GUIO_NONE;
-
-#define GAME_LVFPN(id,name,fname,md5,size,lang,ver,features,gid,platform,interp) { \
- { \
- id, \
- name, \
- AD_ENTRY1s(fname,md5,size), \
- lang, \
- platform, \
- ADGF_NO_FLAGS, \
- GUIO_NONE \
- }, \
- gid, \
- interp, \
- features, \
- ver, \
- }
-
-#define GAME_LVFPNF(id,name,fname,md5,size,lang,ver,features,gid,platform,interp) { \
- { \
- id, \
- name, \
- AD_ENTRY1s(fname,md5,size), \
- lang, \
- platform, \
- ADGF_USEEXTRAASTITLE, \
- GUIO_NONE \
- }, \
- gid, \
- interp, \
- features, \
- ver, \
- }
-
-#define GAME(id,name,md5,ver,gid) GAME_LVFPN(id,name,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V2)
-#define GAME3(id,name,fname,md5,ver,gid) GAME_LVFPN(id,name,fname,md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V3)
-
-#define GAME_P(id,name,md5,ver,gid,platform) GAME_LVFPN(id,name,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_V2)
-
-#define GAME_FP(id,name,md5,ver,flags,gid,platform) GAME_LVFPN(id,name,"logdir",md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V2)
-
-#define GAME_PS(id,name,md5,size,ver,gid,platform) GAME_LVFPN(id,name,"logdir",md5,size,Common::EN_ANY,ver,0,gid,platform,GType_V2)
-
-#define GAME_LPS(id,name,md5,size,lang,ver,gid,platform) GAME_LVFPN(id,name,"logdir",md5,size,lang,ver,0,gid,platform,GType_V2)
-
-#define GAME_LFPS(id,name,md5,size,lang,ver,flags,gid,platform) GAME_LVFPN(id,name,"logdir",md5,size,lang,ver,flags,gid,platform,GType_V2)
-
-#define GAME3_P(id,name,fname,md5,ver,flags,gid,platform) GAME_LVFPN(id,name,fname,md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
-
-#define GAMEpre_P(id,name,fname,md5,ver,gid,platform) GAME_LVFPN(id,name,fname,md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
-
-#define GAMEpre_PS(id,name,fname,md5,size,ver,gid,platform) GAME_LVFPN(id,name,fname,md5,size,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
-
-#define GAME3_PS(id,name,fname,md5,size,ver,flags,gid,platform) GAME_LVFPN(id,name,fname,md5,size,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
-
-#define FANMADE_ILVF(id,name,md5,lang,ver,features) GAME_LVFPNF(id,name,"logdir",md5,-1,lang,ver,(GF_FANMADE|features),GID_FANMADE,Common::kPlatformPC,GType_V2)
-
-#define FANMADE_ISVP(id,name,md5,size,ver,platform) GAME_LVFPNF(id,name,"logdir",md5,size,Common::EN_ANY,ver,GF_FANMADE,GID_FANMADE,platform,GType_V2)
-#define FANMADE_SVP(name,md5,size,ver,platform) FANMADE_ISVP("agi-fanmade",name,md5,size,ver,platform)
-
-#define FANMADE_LVF(name,md5,lang,ver,features) FANMADE_ILVF("agi-fanmade",name,md5,lang,ver,features)
-
-#define FANMADE_LF(name,md5,lang,features) FANMADE_LVF(name,md5,lang,0x2917,features)
-#define FANMADE_IF(id,name,md5,features) FANMADE_ILVF(id,name,md5,Common::EN_ANY,0x2917,features)
-
-#define FANMADE_V(name,md5,ver) FANMADE_LVF(name,md5,Common::EN_ANY,ver,0)
-#define FANMADE_F(name,md5,features) FANMADE_LF(name,md5,Common::EN_ANY,features)
-#define FANMADE_L(name,md5,lang) FANMADE_LF(name,md5,lang,0)
-#define FANMADE_I(id,name,md5) FANMADE_IF(id,name,md5,0)
-
-#define FANMADE(name,md5) FANMADE_F(name,md5,0)
-
-static const AGIGameDescription gameDescriptions[] = {
-
- // AGI Demo 1 (PC) 05/87 [AGI 2.425]
- GAME("agidemo", "Demo 1 1987-05-20", "9c4a5b09cc3564bc48b4766e679ea332", 0x2440, GID_AGIDEMO),
-
- // AGI Demo 2 (IIgs) 1.0C (Censored)
- GAME_P("agidemo", "Demo 2 1987-11-24 1.0C", "580ffdc569ff158f56fb92761604f70e", 0x2917, GID_AGIDEMO, Common::kPlatformApple2GS),
-
- // AGI Demo 2 (PC 3.5") 11/87 [AGI 2.915]
- GAME("agidemo", "Demo 2 1987-11-24 3.5\"", "e8ebeb0bbe978172fe166f91f51598c7", 0x2917, GID_AGIDEMO),
-
- // AGI Demo 2 (PC 5.25") 11/87 [v1] [AGI 2.915]
- GAME("agidemo", "Demo 2 1987-11-24 [version 1] 5.25\"", "852ac303a374df62571642ca1e2d1f0a", 0x2917, GID_AGIDEMO),
-
- // AGI Demo 2 (PC 5.25") 01/88 [v2] [AGI 2.917]
- GAME("agidemo", "Demo 2 1987-11-25 [version 2] 5.25\"", "1503f02086ea9f388e7e041c039eaa69", 0x2917, GID_AGIDEMO),
-
- // AGI Demo 3 (PC) 09/88 [AGI 3.002.102]
- GAME3("agidemo", "Demo 3 1988-09-13", "dmdir", "289c7a2c881f1d973661e961ced77d74", 0x3149, GID_AGIDEMO),
-
- // AGI Demo for Kings Quest III and Space Quest I
- GAME("agidemo", "Demo Kings Quest III and Space Quest I", "502e6bf96827b6c4d3e67c9cdccd1033", 0x2272, GID_AGIDEMO),
-
- // Black Cauldron (Amiga) 2.00 6/14/87
- GAME_P("bc", "2.00 1987-06-14", "7b01694af21213b4727bb94476f64eb5", 0x2440, GID_BC, Common::kPlatformAmiga),
-
- // Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE)
- // Menus not tested
- GAME3_P("bc", "1.0O 1989-02-24 (CE)", "bcdir", "dc09d30b147242692f4f85b9811962db", 0x3149, 0, GID_BC, Common::kPlatformApple2GS),
-
- // Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439]
- GAME("bc", "2.00 1987-06-14", "7f598d4712319b09d7bd5b3be10a2e4a", 0x2440, GID_BC),
-
- // Black Cauldron (Russian)
- GAME_LPS("bc", "", "b7de782dfdf8ea7dde8064f09804bcf5", 357, Common::RU_RUS, 0x2440, GID_BC, Common::kPlatformPC),
-
- // Black Cauldron (PC 5.25") 2.10 11/10/88 [AGI 3.002.098]
- GAME3("bc", "2.10 1988-11-10 5.25\"", "bcdir", "0c5a9acbcc7e51127c34818e75806df6", 0x3149, GID_BC),
-
- // Black Cauldron (PC) 2.10 [AGI 3.002.097]
- GAME3("bc", "2.10", "bcdir", "0de3953c9225009dc91e5b0d1692967b", 0x3149, GID_BC),
-
- // Black Cauldron (CoCo3 360k) [AGI 2.023]
- GAME_PS("bc", "", "51212c54808ade96176f201ae0ac7a6f", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
-
- // Black Cauldron (CoCo3 360k) [AGI 2.072]
- GAME_PS("bc", "updated", "c4e1937f74e8100cd0152b904434d8b4", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
-
-// TODO
-// These aren't supposed to work now as they require unsupported agi engine 2.01
-#if 0
- // Donald Duck's Playground (Amiga) 1.0C
- // Menus not tested
- GAME_P("ddp", "1.0C 1987-04-27", "550971d196f65190a5c760d2479406ef", 0x2272, GID_DDP, Common::kPlatformAmiga),
-
- // Donald Duck's Playground (ST) 1.0A 8/8/86
- // Menus not tested
- GAME("ddp", "1.0A 1986-08-08", "64388812e25dbd75f7af1103bc348596", 0x2272, GID_DDP),
-
- // reported by Filippos (thebluegr) in bugreport #1654500
- // Menus not tested
- GAME_PS("ddp", "1.0C 1986-06-09", "550971d196f65190a5c760d2479406ef", 132, 0x2272, GID_DDP, Common::kPlatformPC),
-#endif
-
- // Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316
- GAME3_PS("goldrush", "1.01 1989-01-13 aka 2.05 1989-03-09", "dirs", "a1d4de3e75c2688c1e2ca2634ffc3bd8", 2399, 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAmiga),
-
- // Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88
- // Menus not tested
- GAME3_P("goldrush", "1.0M 1989-02-28 (CE) aka 2.01 1988-12-22", "grdir", "3f7b9ce62631434389f85371b11921d6", 0x3149, 0, GID_GOLDRUSH, Common::kPlatformApple2GS),
-
- // Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88
- GAME3_P("goldrush", "1.01 1989-01-13 aka 2.01 1988-12-22", "grdir", "4dd4d50480a3d6c206fa227ce8142735", 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAtariST),
-
- // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
- GAME3("goldrush", "2.01 1988-12-22 5.25\"", "grdir", "db733d199238d4009a9e95f11ece34e9", 0x3149, GID_GOLDRUSH),
-
- // Gold Rush! (PC 3.5") 2.01 12/22/88 [AGI 3.002.149]
- GAME3("goldrush", "2.01 1988-12-22 3.5\"", "grdir", "6a285235745f69b4b421403659497216", 0x3149, GID_GOLDRUSH),
-
- // Gold Rush! (PC 3.5", bought from The Software Farm) 3.0 1998-12-22 [AGI 3.002.149]
- GAME3("goldrush", "3.0 1998-12-22 3.5\"", "grdir", "6882b6090473209da4cd78bb59f78dbe", 0x3149, GID_GOLDRUSH),
-
- {
- // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
- {
- "goldrush",
- "2.01 1988-12-22",
- {
- { "grdir", 0, "db733d199238d4009a9e95f11ece34e9", 2399},
- { "vol.0", 0, "4b6423d143674d3757ab1b875d25951d", 25070},
- { NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_GOLDRUSH,
- GType_V3,
- GF_MACGOLDRUSH,
- 0x3149,
- },
-
-
- // Gold Rush! (CoCo3 720k) [AGI 2.023]
- GAME_PS("goldrush", "", "0a41b65efc0cd6c4271e957e6ffbbd8e", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
-
- // Gold Rush! (CoCo3 360k/720k) [AGI 2.072]
- GAME_PS("goldrush", "updated", "c49bf56bf91e31a4601a604e51ef8bfb", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
-
- // King's Quest 1 (Amiga) 1.0U # 2.082
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("kq1", "1.0U 1986", "246c695324f1c514aee2b904fa352fad", 0x2440, GF_MENUS, GID_KQ1, Common::kPlatformAmiga),
-
- // King's Quest 1 (ST) 1.0V
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("kq1", "1.0V 1986", "c3a017e556c4b0eece366a4cd9abb657", 0x2272, GF_MENUS, GID_KQ1, Common::kPlatformAtariST),
-
- // King's Quest 1 (IIgs) 1.0S-88223
- // Menus not tested
- GAME_P("kq1", "1.0S 1988-02-23", "f4277aa34b43d37382bc424c81627617", 0x2272, GID_KQ1, Common::kPlatformApple2GS),
-
- // King's Quest 1 (Mac) 2.0C
- GAME_P("kq1", "2.0C 1987-03-26", "d4c4739d4ac63f7dbd29255425077d48", 0x2440, GID_KQ1, Common::kPlatformMacintosh),
-
- // King's Quest 1 (PC 5.25"/3.5") 2.0F [AGI 2.917]
- GAME("kq1", "2.0F 1987-05-05 5.25\"/3.5\"", "10ad66e2ecbd66951534a50aedcd0128", 0x2917, GID_KQ1),
-
- // King's Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq1", "", "10ad66e2ecbd66951534a50aedcd0128", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
-
- // King's Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq1", "fixed", "4c8ef8b5d2f1b6c1a93e456d1f1ffc74", 768, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
-
- // King's Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("kq1", "updated", "94087178c78933a4af3cd24d1c8dd7b2", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
-
- // King's Quest 2 (IIgs) 2.0A 6/16/88 (CE)
- GAME_P("kq2", "2.0A 1988-06-16 (CE)", "5203c8b95250a2ecfee93ddb99414753", 0x2917, GID_KQ2, Common::kPlatformApple2GS),
-
- // King's Quest 2 (Amiga) 2.0J (Broken)
- GAME_P("kq2", "2.0J 1987-01-29 [OBJECT decrypted]", "b866f0fab2fad91433a637a828cfa410", 0x2440, GID_KQ2, Common::kPlatformAmiga),
-
- // King's Quest 2 (Mac) 2.0R
- GAME_P("kq2", "2.0R 1988-03-23", "cbdb0083317c8e7cfb7ac35da4bc7fdc", 0x2440, GID_KQ2, Common::kPlatformMacintosh),
-
- // King's Quest 2 (PC) 2.1 [AGI 2.411]; entry from DAGII, but missing from Sarien?
- // XXX: any major differences from 2.411 to 2.440?
- GAME("kq2", "2.1 1987-04-10", "759e39f891a0e1d86dd29d7de485c6ac", 0x2440, GID_KQ2),
-
- // King's Quest 2 (PC 5.25"/3.5") 2.2 [AGI 2.426]
- GAME("kq2", "2.2 1987-05-07 5.25\"/3.5\"", "b944c4ff18fb8867362dc21cc688a283", 0x2917, GID_KQ2),
-
- // King's Quest 2 (Russian)
- GAME_LPS("kq2", "", "35211c574ececebdc723b23e35f99275", 543, Common::RU_RUS, 0x2917, GID_KQ2, Common::kPlatformPC),
-
- // King's Quest 2 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq2", "", "b944c4ff18fb8867362dc21cc688a283", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
-
- // King's Quest 2 (CoCo3 360k) [AGI 2.072]
- GAME_PS("kq2", "updated", "f64a606de740a5348f3d125c03e989fe", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
-
- // King's Quest 2 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq2", "fixed", "fb33ac2768a94a89117a270771db465c", 768, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
-
- // King's Quest 3 (Amiga) 1.01 11/8/86
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("kq3", "1.01 1986-11-08", "8ab343306df0e2d98f136be4e8cfd0ef", 0x2440, GF_MENUS, GID_KQ3, Common::kPlatformAmiga),
-
- // King's Quest 3 (ST) 1.02 11/18/86
- // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
- GAME_FP("kq3", "1.02 1986-11-18", "8846df2654302b623217ba8bd6d657a9", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformAtariST),
-
- // King's Quest 3 (Mac) 2.14 3/15/88
- GAME_P("kq3", "2.14 1988-03-15", "7639c0da5ce94848227d409351fabda2", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
-
- // King's Quest 3 (IIgs) 2.0A 8/28/88 (CE)
- GAME_P("kq3", "2.0A 1988-08-28 (CE)", "ac30b7ca5a089b5e642fbcdcbe872c12", 0x2917, GID_KQ3, Common::kPlatformApple2GS),
-
- // King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333
- // Original pauses with ESC, has menus accessible with mouse.
- // ver = 0x3086 -> menus accessible with ESC or mouse, bug #2835581 (KQ3: Game Crash When Leaving Tavern as Fly).
- // ver = 0x3149 -> menus accessible with mouse, ESC pauses game, bug #2835581 disappears.
- GAME3_PS("kq3", "2.15 1989-11-15", "dirs", "8e35bded2bc5cf20f5eec2b15523b155", 1805, 0x3149, 0, GID_KQ3, Common::kPlatformAmiga),
-
- // King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272]
- // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
- GAME_FP("kq3", "1.01 1986-11-08", "9c2b34e7ffaa89c8e2ecfeb3695d444b", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
-
- // King's Quest 3 (Russian)
- GAME_LFPS("kq3", "", "5856dec6ccb9c4b70aee21044a19270a", 390, Common::RU_RUS, 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
-
- // King's Quest 3 (PC 5.25") 2.00 5/25/87 [AGI 2.435]
- GAME("kq3", "2.00 1987-05-25 5.25\"", "18aad8f7acaaff760720c5c6885b6bab", 0x2440, GID_KQ3),
-
- // King's Quest 3 (Mac) 2.14 3/15/88
- // Menus not tested
- GAME_P("kq3", "2.14 1988-03-15 5.25\"", "7650e659c7bc0f1e9f8a410b7a2e9de6", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
-
- // King's Quest 3 (PC 3.5") 2.14 3/15/88 [AGI 2.936]
- GAME("kq3", "2.14 1988-03-15 3.5\"", "d3d17b77b3b3cd13246749231d9473cd", 0x2936, GID_KQ3),
-
- // King's Quest 3 (CoCo3 158k/360k) [AGI 2.023]
- GAME_PS("kq3", "", "5a6be7d16b1c742c369ef5cc64fefdd2", 429, 0x2440, GID_KQ3, Common::kPlatformCoCo3),
-
- // King's Quest 4 (PC 5.25") 2.0 7/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.0 1988-07-27", "kq4dir", "f50f7f997208ca0e35b2650baec43a2d", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 3.5") 2.0 7/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.0 1988-07-27 3.5\"", "kq4dir", "fe44655c42f16c6f81046fdf169b6337", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 3.5") 2.2 9/27/88 [AGI 3.002.086]
- // Menus not tested
- GAME3("kq4", "2.2 1988-09-27 3.5\"", "kq4dir", "7470b3aeb49d867541fc66cc8454fb7d", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 5.25") 2.3 9/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.3 1988-09-27", "kq4dir", "6d7714b8b61466a5f5981242b993498f", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 3.5") 2.3 9/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.3 1988-09-27 3.5\"", "kq4dir", "82a0d39af891042e99ac1bd6e0b29046", 0x3086, GID_KQ4),
-
- // King's Quest 4 (IIgs) 1.0K 11/22/88 (CE)
- // Menus not tested
- GAME3_P("kq4", "1.0K 1988-11-22", "kq4dir", "8536859331159f15012e35dc82cb154e", 0x3086, 0, GID_KQ4, Common::kPlatformApple2GS),
-
- // King's Quest 4 demo (PC) [AGI 3.002.102]
- // Menus not tested
- GAME3("kq4", "Demo 1988-12-20", "dmdir", "a3332d70170a878469d870b14863d0bf", 0x3149, GID_KQ4),
-
- // King's Quest 4 (CoCo3 720k) [AGI 2.023]
- GAME_PS("kq4", "", "9e7729a28e749ca241d2bf71b9b2dbde", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
-
- // King's Quest 4 (CoCo3 360k/720k) [AGI 2.072]
- GAME_PS("kq4", "updated", "1959ca10739edb34069bb504dbd74805", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
-
- // Leisure Suit Larry 1 (PC 5.25"/3.5") 1.00 6/1/87 [AGI 2.440]
- GAME("lsl1", "1.00 1987-06-01 5.25\"/3.5\"", "1fe764e66857e7f305a5f03ca3f4971d", 0x2440, GID_LSL1),
-
- // Leisure Suit Larry 1 Polish
- GAME_LPS("lsl1", "2.00 2001-12-11", "7ba1fccc46d27c141e704706c1d0a85f", 303, Common::PL_POL, 0x2440, GID_LSL1, Common::kPlatformPC),
-
- // Leisure Suit Larry 1 Polish - Demo
- GAME_LPS("lsl1", "Demo", "3b2f564306c401dff6334441df967ddd", 666, Common::PL_POL, 0x2917, GID_LSL1, Common::kPlatformPC),
-
- // Leisure Suit Larry 1 (ST) 1.04 6/18/87
- GAME_P("lsl1", "1.04 1987-06-18", "8b579f8673fe9448c2538f5ed9887cf0", 0x2440, GID_LSL1, Common::kPlatformAtariST),
-
- // Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy
- GAME_P("lsl1", "1.05 1987-06-26", "3f5d26d8834ca49c147fb60936869d56", 0x2440, GID_LSL1, Common::kPlatformAmiga),
-
- // Leisure Suit Larry 1 (IIgs) 1.0E
- GAME_P("lsl1", "1.0E 1987", "5f9e1dd68d626c6d303131c119582ad4", 0x2440, GID_LSL1, Common::kPlatformApple2GS),
-
- // Leisure Suit Larry 1 (Mac) 1.05 6/26/87
- GAME_P("lsl1", "1.05 1987-06-26", "8a0076429890531832f0dc113285e31e", 0x2440, GID_LSL1, Common::kPlatformMacintosh),
-
- // Leisure Suit Larry 1 (CoCo3 158k/360k) [AGI 2.072]
- GAME_PS("lsl1", "", "a2de1fe76565c3e8b40c9d036b5e5612", 198, 0x2440, GID_LSL1, Common::kPlatformCoCo3),
-
- // Manhunter NY (ST) 1.03 10/20/88
- GAME3_P("mh1", "1.03 1988-10-20", "mhdir", "f2d58056ad802452d60776ee920a52a6", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
-
- // Manhunter NY (IIgs) 2.0E 10/05/88 (CE)
- GAME3_P("mh1", "2.0E 1988-10-05 (CE)", "mhdir", "2f1509f76f24e6e7d213f2dadebbf156", 0x3149, 0, GID_MH1, Common::kPlatformApple2GS),
-
- // Manhunter NY (Amiga) 1.06 3/18/89
- GAME3_P("mh1", "1.06 1989-03-18", "dirs", "92c6183042d1c2bb76236236a7d7a847", 0x3149, GF_OLDAMIGAV20, GID_MH1, Common::kPlatformAmiga),
-
- // reported by Filippos (thebluegr) in bugreport #1654500
- // Manhunter NY (PC 5.25") 1.22 8/31/88 [AGI 3.002.107]
- GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "0c7b86f05fe02c2e26cff1b07450b82a", 2123, 0x3149, 0, GID_MH1, Common::kPlatformPC),
-
- // Manhunter NY (PC 3.5") 1.22 8/31/88 [AGI 3.002.102]
- GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "5b625329021ad49fd0c1d6f2d6f54bba", 2141, 0x3149, 0, GID_MH1, Common::kPlatformPC),
-
- // Manhunter NY (CoCo3 720k) [AGI 2.023]
- GAME_PS("mh1", "", "b968285caf2f591c78dd9c9e26ab8974", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
-
- // Manhunter NY (CoCo3 360k/720k) [AGI 2.072]
- GAME_PS("mh1", "updated", "d47da950c62289f8d4ccf36af73365f2", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
-
- // Manhunter SF (ST) 1.0 7/29/89
- GAME3_P("mh2", "1.0 1989-07-29", "mh2dir", "5e3581495708b952fea24438a6c7e040", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
-
- // Manhunter SF (Amiga) 3.06 8/17/89 # 2.333
- GAME3_PS("mh2", "3.06 1989-08-17", "dirs", "b412e8a126368b76696696f7632d4c16", 2573, 0x3086, GF_OLDAMIGAV20, GID_MH2, Common::kPlatformAmiga),
-
- // Manhunter SF (PC 5.25") 3.03 8/17/89 [AGI 3.002.149]
- GAME3("mh2", "3.03 1989-08-17 5.25\"", "mh2dir", "b90e4795413c43de469a715fb3c1fa93", 0x3149, GID_MH2),
-
- // Manhunter SF (PC 3.5") 3.02 7/26/89 [AGI 3.002.149]
- GAME3("mh2", "3.02 1989-07-26 3.5\"", "mh2dir", "6fb6f0ee2437704c409cf17e081ba152", 0x3149, GID_MH2),
-
- // Manhunter SF (CoCo3 720k) [AGI 2.023]
- GAME_PS("mh2", "", "acaaa577e10d1753c5a74f6ae1d858d4", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
-
- // Manhunter SF (CoCo3 720k) [AGI 2.072]
- GAME_PS("mh2", "updated", "c64875766700196e72a92359f70f45a9", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
-
- // Mickey's Space Adventure
- // Preagi game
- GAMEpre_P("mickey", "", "1.pic", "b6ec04c91a05df374792872c4d4ce66d", 0x0000, GID_MICKEY, Common::kPlatformPC),
-
-#if 0
- // Mixed-Up Mother Goose (Amiga) 1.1
- // Problematic: crashes
- // Menus not tested
- GAME3_PS("mixedup", "1.1 1986-12-10", "dirs", "5c1295fe6daaf95831195ba12894dbd9", 2021, 0x3086, 0, GID_MIXEDUP, Common::kPlatformAmiga),
-#endif
-
- // Mixed Up Mother Goose (IIgs)
- GAME_P("mixedup", "1987", "3541954a7303467c6df87665312ffb6a", 0x2917, GID_MIXEDUP, Common::kPlatformApple2GS),
-
- // Mixed-Up Mother Goose (PC) [AGI 2.915]
- GAME("mixedup", "1987-11-10", "e524655abf9b96a3b179ffcd1d0f79af", 0x2917, GID_MIXEDUP),
-
- // Mixed-Up Mother Goose (CoCo3 360k) [AGI 2.072]
- GAME_PS("mixedup", "", "44e63e9b4d4822a31edea0e8a7e7eac4", 606, 0x2440, GID_MIXEDUP, Common::kPlatformCoCo3),
-
- // Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915]
- GAME("pq1", "2.0E 1987-11-17", "2fd992a92df6ab0461d5a2cd83c72139", 0x2917, GID_PQ1),
-
- // Police Quest 1 (Mac) 2.0G 12/3/87
- GAME_P("pq1", "2.0G 1987-12-03", "805750b66c1c5b88a214e67bfdca17a1", 0x2440, GID_PQ1, Common::kPlatformMacintosh),
-
- // Police Quest 1 (IIgs) 2.0B-88421
- GAME_P("pq1", "2.0B 1988-04-21", "e7c175918372336461e3811d594f482f", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
-
- // Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310
- GAME3_PS("pq1", "2.0B 1989-02-22", "dirs", "cfa93e5f2aa7378bddd10ad6746a2ffb", 1613, 0x3149, 0, GID_PQ1, Common::kPlatformAmiga),
-
- // Police Quest 1 (IIgs) 2.0A-88318
- GAME_P("pq1", "2.0A 1988-03-18", "8994e39d0901de3d07cecfb954075bb5", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
-
- // Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911]
- GAME("pq1", "2.0A 1987-10-23", "b9dbb305092851da5e34d6a9f00240b1", 0x2917, GID_PQ1),
-
- // Police Quest 1 (Russian)
- GAME_LPS("pq1", "", "604cc8041d24c4c7e5fa8baf386ef76e", 360, Common::RU_RUS, 0x2917, GID_PQ1, Common::kPlatformPC),
-
- // Police Quest 1 2.0G 12/3/87
- GAME("pq1", "2.0G 1987-12-03 5.25\"/ST", "231f3e28170d6e982fc0ced4c98c5c1c", 0x2440, GID_PQ1),
-
- // Police Quest 1 (PC) 2.0G 12/3/87; entry from DAGII, but missing from Sarien?
- // not sure about disk format -- dsymonds
- GAME("pq1", "2.0G 1987-12-03", "d194e5d88363095f55d5096b8e32fbbb", 0x2917, GID_PQ1),
-
- // Police Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("pq1", "", "28a077041f75aab78f66804800940085", 375, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
-
- // Police Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("pq1", "updated", "63b9a9c6eec154751dd446cd3693e0e2", 768, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
-
- // Space Quest 1 (ST) 1.1A
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.1A 1986-02-06", "6421fb64b0e6604c9dd065975d9279e9", 0x2440, GF_MENUS, GID_SQ1, Common::kPlatformAtariST),
-
- // Space Quest 1 (PC 360k) 1.1A [AGI 2.272]
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.1A 1986-11-13", "8d8c20ab9f4b6e4817698637174a1cb6", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (PC 720k) 1.1A [AGI 2.272]
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.1A 720kb", "0a92b1be7daf3bb98caad3f849868aeb", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (Amiga) 1.2 # 2.082
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.2 1986", "0b216d931e95750f1f4837d6a4b821e5", 0x2440, GF_MENUS | GF_OLDAMIGAV20, GID_SQ1, Common::kPlatformAmiga),
-
- // Space Quest 1 (Mac) 1.5D
- GAME_P("sq1", "1.5D 1987-04-02", "ce88419aadd073d1c6682d859b3d8aa2", 0x2440, GID_SQ1, Common::kPlatformMacintosh),
-
- // Space Quest 1 (IIgs) 2.2
- GAME_P("sq1", "2.2 1987", "64b9b3d04c1066d36e6a6e56187a83f7", 0x2917, GID_SQ1, Common::kPlatformApple2GS),
-
- // Space Quest 1 (PC) 1.0X [AGI 2.089]
- // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
- GAME_FP("sq1", "1.0X 1986-09-24", "af93941b6c51460790a9efa0e8cb7122", 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (Russian)
- GAME_LFPS("sq1", "", "a279eb8ddbdefdb1ea6adc827a1d632a", 372, Common::RU_RUS, 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (PC 5.25"/3.5") 2.2 [AGI 2.426/2.917]
- GAME("sq1", "2.2 1987-05-07 5.25\"/3.5\"", "5d67630aba008ec5f7f9a6d0a00582f4", 0x2440, GID_SQ1),
-
- // Space Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("sq1", "", "5d67630aba008ec5f7f9a6d0a00582f4", 372, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
-
- // Space Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("sq1", "fixed", "ca822b768b6462e410423ea7f498daee", 768, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
-
- // Space Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("sq1", "updated", "7fa54e6bb7ffeb4cf20eca39d86f5fb2", 387, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
-
- // Space Quest 2 (PC 3.5") 2.0D [AGI 2.936]
- GAME("sq2", "2.0D 1988-03-14 3.5\"", "85390bde8958c39830e1adbe9fff87f3", 0x2936, GID_SQ2),
-
- // Space Quest 2 (IIgs) 2.0A 7/25/88 (CE)
- GAME_P("sq2", "2.0A 1988-07-25 (CE)", "5dfdac98dd3c01fcfb166529f917e911", 0x2936, GID_SQ2, Common::kPlatformApple2GS),
-
- {
- // Space Quest 2 (Amiga) 2.0F
- {
- "sq2",
- "2.0F 1986-12-09 [VOL.2->PICTURE.16 broken]",
- {
- { "logdir", 0, "28add5125484302d213911df60d2aded", 426},
- { "object", 0, "5dc52be721257719f4b311a84ce22b16", 372},
- { NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_SQ2,
- GType_V2,
- 0,
- 0x2936,
- },
-
-
- // Space Quest 2 (Mac) 2.0D
- GAME_P("sq2", "2.0D 1988-04-04", "bfbebe0b59d83f931f2e1c62ce9484a7", 0x2936, GID_SQ2, Common::kPlatformMacintosh),
-
- // reported by Filippos (thebluegr) in bugreport #1654500
- // Space Quest 2 (PC 5.25") 2.0A [AGI 2.912]
- GAME_PS("sq2", "2.0A 1987-11-06 5.25\"", "ad7ce8f800581ecc536f3e8021d7a74d", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
-
- // Space Quest 2 (Russian)
- GAME_LPS("sq2", "", "ba21c8934caf28e3ba45ce7d1cd6b041", 423, Common::RU_RUS, 0x2917, GID_SQ2, Common::kPlatformPC),
-
- // Space Quest 2 (PC 3.5") 2.0A [AGI 2.912]
- GAME_PS("sq2", "2.0A 1987-11-06 3.5\"", "6c25e33d23b8bed42a5c7fa63d588e5c", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
-
- // Space Quest 2 (PC 5.25"/ST) 2.0C/A [AGI 2.915]
- // Menus not tested
- GAME("sq2", "2.0C/A 5.25\"/ST", "bd71fe54869e86945041700f1804a651", 0x2917, GID_SQ2),
-
- // Space Quest 2 (PC 3.5") 2.0F [AGI 2.936]
- GAME("sq2", "2.0F 1989-01-05 3.5\"", "28add5125484302d213911df60d2aded", 0x2936, GID_SQ2),
-
- // Space Quest 2 (CoCo3 360k) [AGI 2.023]
- GAME_PS("sq2", "", "12973d39b892dc9d280257fd271e9597", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
-
- // Space Quest 2 (CoCo3 360k) [AGI 2.072]
- GAME_PS("sq2", "updated", "d24f19b047e65e1763eff4b46f3d50df", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
-
- // Troll's Tale
- GAMEpre_PS("troll", "", "troll.img", "62903f264b3d849be4214b3a5c42a2fa", 184320, 0x0000, GID_TROLL, Common::kPlatformPC),
-
- // Winnie the Pooh in the Hundred Acre Wood
- GAMEpre_P("winnie", "", "title.pic", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformPC),
-
- // Winnie the Pooh in the Hundred Acre Wood (Amiga)
- GAMEpre_P("winnie", "", "title", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformAmiga),
-
- // Winnie the Pooh in the Hundred Acre Wood (C64)
- GAMEpre_P("winnie", "", "title.pic", "d4eb97cffc866110f71e1ec9f84fe643", 0x0000, GID_WINNIE, Common::kPlatformC64),
-
- // Winnie the Pooh in the Hundred Acre Wood (Apple //gs)
- GAMEpre_P("winnie", "", "title.pic", "45e06010a3c61d78f4661103c901ae11", 0x0000, GID_WINNIE, Common::kPlatformApple2GS),
-
- // Xmas Card 1986 (PC) [AGI 2.272]
- GAME("xmascard", "1986-11-13 [version 1]", "3067b8d5957e2861e069c3c0011bd43d", 0x2272, GID_XMASCARD),
-
- // Xmas Card 1986 (CoCo3 360k) [AGI 2.072]
- GAME_PS("xmascard", "", "25ad35e9628fc77e5e0dd35852a272b6", 768, 0x2440, GID_XMASCARD, Common::kPlatformCoCo3),
-
- FANMADE_F("2 Player Demo", "4279f46b3cebd855132496476b1d2cca", GF_AGIMOUSE),
- FANMADE("AGI Contest 1 Template", "d879aed25da6fc655564b29567358ae2"),
- FANMADE("AGI Contest 2 Template", "5a2fb2894207eff36c72f5c1b08bcc07"),
- FANMADE("AGI Mouse Demo 0.60 demo 1", "c07e2519de674c67386cb2cc6f2e3904"),
- FANMADE("AGI Mouse Demo 0.60 demo 2", "cc49d8b88ed6faf4f53ce92c84e0fe1b"),
- FANMADE("AGI Mouse Demo 0.70", "3497c291e4afb6f758e61740678a2aec"),
- FANMADE_F("AGI Mouse Demo 1.00", "20397f0bf0ef936f416bb321fb768fc7", GF_AGIMOUSE),
- FANMADE_F("AGI Mouse Demo 1.10", "f4ad396b496d6167635ad0b410312ab8", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("AGI Piano (v1.0)", "8778b3d89eb93c1d50a70ef06ef10310"),
- FANMADE("AGI Quest (v1.46-TJ0)", "1cf1a5307c1a0a405f5039354f679814"),
- FANMADE_I("tetris", "", "7a874e2db2162e7a4ce31c9130248d8a"),
- FANMADE_V("AGI Trek (Demo)", "c02882b8a8245b629c91caf7eb78eafe", 0x2440),
- FANMADE_F("AGI256 Demo", "79261ac143b2e2773b2753674733b0d5", GF_AGI256),
- FANMADE_F("AGI256-2 Demo", "3cad9b3aff1467cebf0c5c5b110985c5", GF_AGI256_2),
- FANMADE_LF("Abrah: L'orphelin de l'espace (v1.2)", "b7b6d1539e14d5a26fa3088288e1badc", Common::FR_FRA, GF_AGIPAL),
- FANMADE("Acidopolis", "7017db1a4b726d0d59e65e9020f7d9f7"),
- FANMADE("Agent 0055 (v1.0)", "c2b34a0c77acb05482781dda32895f24"),
- FANMADE("Agent 06 vs. The Super Nazi", "136f89ca9f117c617e88a85119777529"),
- FANMADE("Agent Quest", "59e49e8f72058a33c00d60ee1097e631"),
- FANMADE("Al Pond - On Holiday (v1.0)", "a84975496b42d485920e886e92eed68b"),
- FANMADE("Al Pond - On Holiday (v1.1)", "7c95ac4689d0c3bfec61e935f3093634"),
- FANMADE("Al Pond - On Holiday (v1.3)", "8f30c260de9e1dd3d8b8f89cc19d2633"),
- FANMADE("Al Pond 1 - Al Lives Forever (v1.0)", "e8921c3043b749b056ff51f56d1b451b"),
- FANMADE("Al Pond 1 - Al Lives Forever (v1.3)", "fb4699474054962e0dbfb4cf12ca52f6"),
- FANMADE("Apocalyptic Quest (v0.03 Teaser)", "42ced528b67965d3bc3b52c635f94a57"),
- FANMADE_F("Apocalyptic Quest (v4.00 Alpha 1)", "e15581628d84949b8d352d224ec3184b", GF_AGIMOUSE),
- FANMADE_F("Apocalyptic Quest (v4.00 Alpha 2)", "0eee850005860e46345b38fea093d194", GF_AGIMOUSE),
- FANMADE_F("Band Quest (Demo)", "7326abefd793571cc17ed0db647bdf34", GF_AGIMOUSE),
- FANMADE_F("Band Quest (Early Demo)", "de4758dd34676b248c8301b32d93bc6f", GF_AGIMOUSE),
- FANMADE("Beyond the Titanic 2", "9b8de38dc64ffb3f52b7877ea3ebcef9"),
- FANMADE("Biri Quest 1", "1b08f34f2c43e626c775c9d6649e2f17"),
- FANMADE("Bob The Farmboy", "e4b7df9d0830addee5af946d380e66d7"),
- FANMADE_F("Boring Man 1: The Toad to Robinland", "d74481cbd227f67ace37ce6a5493039f", GF_AGIMOUSE),
- FANMADE_F("Boring Man 2: Ho Man! This Game Sucks!", "250032ba105bdf7c1bc4fed767c2d37e", GF_AGIMOUSE),
- FANMADE("Botz", "a8fabe4e807adfe5ec02bfec6d983695"),
- FANMADE("Brian's Quest (v1.0)", "0964aa79b9cdcff7f33a12b1d7e04b9c"),
- FANMADE("CPU-21 (v1.0)", "35b7cdb4d17e890e4c52018d96e9cbf4"),
- FANMADE_I("caitlyn", "Demo", "5b8a3cdb2fc05469f8119d49f50fbe98"),
- FANMADE_I("caitlyn", "", "818469c484cae6dad6f0e9a353f68bf8"),
- FANMADE("Car Driver (v1.1)", "2311611d2d36d20ccc9da806e6cba157"),
- FANMADE("Cloak of Darkness (v1.0)", "5ba6e18bf0b53be10db8f2f3831ee3e5"),
- FANMADE("Coco Coq (English) - Coco Coq In Grostesteing's Base (v.1.0.3)", "97631f8e710544a58bd6da9e780f9320"),
- FANMADE_L("Coco Coq (French) - Coco Coq Dans la Base de Grostesteing (v1.0.2)", "ef579ebccfe5e356f9a557eb3b2d8649", Common::FR_FRA),
- FANMADE("Corby's Murder Mystery (v1.0)", "4ebe62ac24c5a8c7b7898c8eb070efe5"),
- FANMADE_F("DG: The AGIMouse Adventure (English v1.1)", "efe453b92bc1487ea69fbebede4d5f26", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_LF("DG: The AGIMouse Adventure (French v1.1)", "eb3d17ca466d672cbb95947e8d6e846a", Common::FR_FRA, GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("DG: The Adventure Game (English v1.1)", "0d6376d493fa7a21ec4da1a063e12b25"),
- FANMADE_L("DG: The Adventure Game (French v1.1)", "258bdb3bb8e61c92b71f2f456cc69e23", Common::FR_FRA),
- FANMADE("Dashiki (16 Colors)", "9b2c7b9b0283ab9f12bedc0cb6770a07"),
- FANMADE_F("Dashiki (256 Colors)", "c68052bb209e23b39b55ff3d759958e6", GF_AGIMOUSE|GF_AGI256),
- FANMADE("Date Quest 1 (v1.0)", "ba3dcb2600645be53a13170aa1a12e69"),
- FANMADE("Date Quest 2 (v1.0 Demo)", "1602d6a2874856e928d9a8c8d2d166e9"),
- FANMADE("Date Quest 2 (v1.0)", "f13f6fc85aa3e6e02b0c20408fb63b47"),
- FANMADE("Dave's Quest (v0.07)", "f29c3660de37bacc1d23547a167f27c9"),
- FANMADE("Dave's Quest (v0.17)", "da3772624cc4a86f7137db812f6d7c39"),
- FANMADE("Disco Nights (Demo)", "dc5a2b21182ba38bdcd992a3a978e690"),
- FANMADE("Dogs Quest - The Quest for the Golden Bone (v1.0)", "f197357edaaea0ff70880602d2f09b3e"),
- FANMADE("Dr. Jummybummy's Space Adventure", "988bd81785f8a452440a2a8ac67f96aa"),
- FANMADE("Ed Ward", "98be839b9f30cbedea4c9cee5442d827"),
- FANMADE("Elfintard", "c3b847e9e9e978af9708df76a0751dc2"),
- FANMADE("Enclosure (v1.01)", "f08e66fee9ecdde77db7ee9a10c96ba2"),
- FANMADE("Enclosure (v1.03)", "e4a0613ed02401502e506ba3565a8c40"),
- FANMADE_SVP("Enclosure", "fe98e6126db74c6cc6fd8fe395cc6e8c", 345, 0x2440, Common::kPlatformCoCo3),
- FANMADE("Epic Fighting (v0.1)", "aff24a1b3bdd676187685c4d95ba4294"),
- FANMADE("Escape Quest (v0.0.3)", "2346b65619b1da0298b715b06d1a45a1"),
- FANMADE("Escape from the Desert (beta 1)", "dfdc634d340854bd6ece28024010758d"),
- FANMADE("Escape from the Salesman", "e723ca4fe0f6f56affe039fbb4dbeb6c"),
- FANMADE("Fu$k Quest 1 (final)", "1cd0587422313f6ca77d6a95988e88ed"),
- FANMADE("Fu$k Quest 1", "1cd0587422313f6ca77d6a95988e88ed"),
- FANMADE("Fu$k Quest 2 - Romancing the Bone (Teaser)", "d288355d71d9bb1639260ccaa3b2fbfe"),
- FANMADE("Fu$k Quest 2 - Romancing the Bone", "294beeb7765c7ea6b05ed7b9bf7bff4f"),
- FANMADE("Gennadi Tahab Autot - Mission Pack 1 - Kuressaare", "bfa5fe71978e6ccf3d4eedd430124015"),
- FANMADE("Go West, Young Hippie", "ff31484ea465441cb5f3a0f8e956b716"),
- FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"),
-
- {
- // Groza
- {
- "agi-fanmade",
- "Groza (russian) [AGDS sample]",
- AD_ENTRY1("logdir", "421da3a18004122a966d64ab6bd86d2e"),
- Common::RU_RUS,
- Common::kPlatformPC,
- ADGF_USEEXTRAASTITLE,
- GUIO_NONE
- },
- GID_FANMADE,
- GType_V2,
- GF_AGDS,
- 0x2440,
- },
-
- {
- // Get Outta SQ
- {
- "agi-fanmade",
- "Get Outta Space Quest",
- AD_ENTRY1("logdir", "aaea5b4a348acb669d13b0e6f22d4dc9"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_USEEXTRAASTITLE,
- GUIO_NONE
- },
- GID_GETOUTTASQ,
- GType_V2,
- 0,
- 0x2440,
- },
-
- FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE),
- FANMADE("Hank's Quest (v1.0 English) - Victim of Society", "64c15b3d0483d17888129100dc5af213"),
- FANMADE("Hank's Quest (v1.1 English) - Victim of Society", "86d1f1dd9b0c4858d096e2a60cca8a14"),
- FANMADE_L("Hank's Quest (v1.81 Dutch) - Slachtoffer Van Het Gebeuren", "41e53972d55ff3dff9e90d15fe1b659f", Common::NL_NLD),
- FANMADE("Hank's Quest (v1.81 English) - Victim of Society", "7a776383282f62a57c3a960dafca62d1"),
- FANMADE("Herbao (v0.2)", "6a5186fc8383a9060517403e85214fc2"),
- FANMADE_F("Hitler's Legacy (v.0004q)", "a412881269ba34584bd0a3268e5a9863", GF_AGIMOUSE),
- FANMADE("Hobbits", "4a1c1ef3a7901baf0ab45fde0cfadd89"),
- FANMADE_F("Isabella Coq - A Present For My Dad", "55c6819f2330c4d5d6459874c9f123d9", GF_AGIMOUSE),
- FANMADE("Jack & Julia - VAMPYR", "8aa0b9a26f8d5a4421067ab8cc3706f6"),
- FANMADE("Jeff's Quest (v.5 alpha Jun 1)", "10f1720eed40c12b02a0f32df3e72ded"),
- FANMADE("Jeff's Quest (v.5 alpha May 31)", "51ff71c0ed90db4e987a488ed3bf0551"),
- FANMADE("Jen's Quest (Demo 1)", "361afb5bdb6160213a1857245e711939"),
- FANMADE("Jen's Quest (Demo 2)", "3c321eee33013b289ab8775449df7df2"),
- FANMADE("Jiggy Jiggy Uh! Uh!", "bc331588a71e7a1c8840f6cc9b9487e4"),
- FANMADE("Jimmy In: The Alien Attack (v0.1)", "a4e9db0564a494728de7873684a4307c"),
- FANMADE("Joe McMuffin In \"What's Cooking, Doc\" (v1.0)", "8a3de7e61a99cb605fa6d233dd91c8e1"),
- FANMADE_LVF("Jolimie, le Village Maudit (v0.5)", "21818501636b3cb8ad5de5c1a66de5c2", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_LVF("Jolimie, le Village Maudit (v1.1)", "68d7aef1161bb5972fe03efdf29ccb7f", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("Journey Of Chef", "aa0a0b5a6364801ae65fdb96d6741df5"),
- FANMADE("Jukebox (v1.0)", "c4b9c5528cc67f6ba777033830de7751"),
- FANMADE("Justin Quest (v1.0 in development)", "103050989da7e0ffdc1c5e1793a4e1ec"),
- FANMADE("J\xf5ulumaa (v0.05) (Estonian)", "53982ecbfb907e41392b3961ad1c3475"),
- FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Mar 26)", "a25d7379d281b1b296d4785df90a8e78"),
- FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Aug 16)", "6b4f796d0421d2e12e501b511962e03a"),
- FANMADE("Lasse Holm: The Quest for Revenge (v1.0)", "f9fbcc8a4ef510bfbb92423296ff4abb"),
- FANMADE("Lawman for Hire", "c78b28bfd3767dd455b992cd8b7854fa"),
- FANMADE("Lefty Goes on Vacation (Not in The Right Place)", "ccdc49a33870310b01f2c48b8a1f3c34"),
- FANMADE("Les Ins\xe3parables (v1.0)", "4b780887cab0ecabc5eca319acb3acf2"),
- FANMADE("Little Pirate (Demo 2 v0.6)", "437068efe4ec32d436da09d6f2ea56e1"),
- FANMADE("Lost Eternity (v1.0)", "95f15c5632feb8a39e9ca3d9af35fcc9"),
- FANMADE("MD Quest - The Search for Michiel (v0.10)", "2a6fcb21d2b5e4144c38ed817fabe8ee"),
- FANMADE("Maale Adummin Quest", "ddfbeb33feb7cf78504fe4dba14ec63b"),
- FANMADE("Monkey Man", "2322d03f997e8cc235d4578efff69cfa"),
- FANMADE_F("Napalm Quest (v0.5)", "b659afb491d967bb34810d1c6ce22093", GF_AGIMOUSE),
- FANMADE("Naturette 1 (English v1.2)", "0a75884e7f010974a230bdf269651117"),
- FANMADE("Naturette 1 (English v1.3)", "f15bbf999ac55ebd404aa1eb84f7c1d9"),
- FANMADE_L("Naturette 1 (French v1.2)", "d3665622cc41aeb9c7ecf4fa43f20e53", Common::FR_FRA),
- FANMADE_F("Naturette 2: Daughter of the Moon (v1.0)", "bdf76a45621c7f56d1c9d40292c6137a", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_F("Naturette 3: Adventure in Treeworld (v1.0a)", "6dbb0e7fc75fec442e6d9e5a06f1530e", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_F("Naturette 4: From a Planet to Another Planet (Not Finished)", "13be8cd9cf35aeff0a39b8757057fbc8", GF_AGIMOUSE),
- // FIXME: Actually Naturette 4 has both English and French language support built into it. How to add that information?
- FANMADE_F("Naturette 4: From a Planet to Another Planet (2007-10-05)", "8253706b6ef5423a79413b216760297c", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("New AGI Hangman Test", "d69c0e9050ccc29fd662b74d9fc73a15"),
- FANMADE("Nick's Quest - In Pursuit of QuakeMovie (v2.1 Gold)", "e29cbf9222551aee40397fabc83eeca0"),
- FANMADE_F("Open Mic Night (v0.1)", "70000a2f67aac27d1133d019df70246d", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("Operation: Recon", "0679ce8405411866ccffc8a6743370d0"),
- FANMADE("Patrick's Quest (Demo v1.0)", "f254f5b894b98fec5f92acc07fb62841"),
- FANMADE("Phantasmagoria", "87d20c1c11aee99a4baad3797b63146b"),
- FANMADE("Pharaoh Quest (v0.0)", "51c630899d076cf799e573dadaa2276d"),
- FANMADE("Phil's Quest - the Search for Tolbaga", "5e7ca45c360e03164b8358e49900c588"),
- FANMADE("Pinkun Maze Quest (v0.1)", "148ff0843af389928b3939f463bfd20d"),
- FANMADE("Pirate Quest", "bb612a919ed2b9ea23bbf03ce69fed42"),
- FANMADE("Pothead (v0.1)", "d181101385d3a45082f418cd4b3c5b01"),
- FANMADE("President's Quest", "4937d0e8ecadb7888faeb347799b0388"),
- FANMADE("Prince Quest", "266248d75c3130c8ccc9c9bf2ad30a0d"),
- FANMADE("Professor (English) - The Professor is Missing (Mar 17)", "6232de31cc204affdf2e92dfe3dc0e4d"),
- FANMADE("Professor (English) - The Professor is Missing (Mar 22)", "b5fcf0ca2f0d1c073be82f01e2170961"),
- FANMADE_L("Professor (French) - Le Professeur a Disparu", "7d9f8a4d4610bb9b0b97caa17590c2d3", Common::FR_FRA),
- FANMADE("Quest for Glory VI - Hero's Adventure", "d26765c3075064c80d284c5e06e33a7e"),
- FANMADE("Quest for Home", "d2895dc1cd3930f2489af0f843b144b3"),
- FANMADE("Quest for Ladies (demo v1.1 Apr 1)", "3f6e02f16e1154a0daf296c8895edd97"),
- FANMADE("Quest for Ladies (demo v1.1 Apr 6)", "f75e7b6a0769a3fa926eea0854711591"),
- FANMADE("Quest for Piracy 1 - Enter the Silver Pirate (v0.15)", "d23f5c2a26f6dc60c686f8a2436ea4a6"),
- FANMADE("Quest for a Record Deal", "f4fbd7abf056d2d3204f790da5ac89ab"),
- FANMADE("Ralph's Quest (v0.1)", "5cf56378aa01a26ec30f25295f0750ca"),
- FANMADE("Residence 44 Quest (Dutch v0.99)", "7c5cc64200660c70240053b33d379d7d"),
- FANMADE("Residence 44 Quest (English v0.99)", "fe507851fddc863d540f2bec67cc67fd"),
- FANMADE("Residence 44 Quest (English v1.0a)", "f99e3f69dc8c77a45399da9472ef5801"),
- FANMADE("SQ2Eye (v0.3)", "2be2519401d38ad9ce8f43b948d093a3"),
- // FANMADE("SQ2Eye (v0.4)", "2be2519401d38ad9ce8f43b948d093a3"),
- FANMADE("SQ2Eye (v0.41)", "f0e82c55f10eb3542d7cd96c107ae113"),
- FANMADE("SQ2Eye (v0.42)", "d7beae55f6328ef8b2da47b1aafea40c"),
- FANMADE("SQ2Eye (v0.43)", "2a895f06e45de153bb4b77c982009e06"),
- FANMADE("SQ2Eye (v0.44)", "5174fc4b6d8a477ba0ff0575cd64e0aa"),
- FANMADE("SQ2Eye (v0.45)", "6e06f8bb7b90ce6f6aabf1a0e620159c"),
- FANMADE("SQ2Eye (v0.46)", "bf0ad7a035ff9113951d09d1efe380c4"),
- FANMADE("SQ2Eye (v0.47)", "85dc3be1d33ff932c292b74f9037abaa"),
- FANMADE("SQ2Eye (v0.48)", "587574252972a5b5c070a647973a9b4a"),
- FANMADE("SQ2Eye (v0.481)", "fc9234beb49804ae869696ce5af8ef30"),
- FANMADE("SQ2Eye (v0.482)", "3ed84b7b87fa6840f25c15f250a11ffb"),
- FANMADE("SQ2Eye (v0.483)", "647c31298d3f9cda641231b893e347c0"),
- FANMADE("SQ2Eye (v0.484)", "f2c86fae7b9046d408c62c8c49a4b882"),
- FANMADE("SQ2Eye (v0.485)", "af59e36bc28f44545458b68a93e91e67"),
- FANMADE("SQ2Eye (v0.486)", "3fd86436e93456770dbdd4593eded70a"),
- FANMADE("Save Santa (v1.0)", "4644f6beb5802081772f14be56ae196c"),
- FANMADE("Save Santa (v1.3)", "f8afdb6efc5af5e7c0228b44633066af"),
- FANMADE("Schiller (preview 1)", "ade39dea968c959cfebe1cf935d653e9"),
- FANMADE("Schiller (preview 2)", "62cd1f8fc758bf6b4aa334e553624cef"),
- FANMADE_IF("serguei1", "v1.0", "b86725f067e456e10cdbdf5f58e01dec", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_IF("serguei1", "v1.1 2002 Sep 5", "91975c1fb4b13b0f9a8e9ff74731030d", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_IF("serguei1", "v1.1 2003 Apr 10", "91975c1fb4b13b0f9a8e9ff74731030d", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_IF("serguei2", "v0.1.1 Demo", "906ccbc2ddedb29b63141acc6d10cd28", GF_AGIMOUSE),
- FANMADE_IF("serguei2", "v1.3.1 Demo (March 22nd 2008)", "ad1308fcb8f48723cd388e012ebf5e20", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("Shifty (v1.0)", "2a07984d27b938364bf6bd243ac75080"),
- FANMADE_F("Sliding Tile Game (v1.00)", "949bfff5d8a81c3139152eed4d84ca75", GF_AGIMOUSE),
- FANMADE("Snowboarding Demo (v1.0)", "24bb8f29f1eddb5c0a099705267c86e4"),
- FANMADE("Solar System Tour", "b5a3d0f392dfd76a6aa63f3d5f578403"),
- FANMADE("Sorceror's Appraisal", "fe62615557b3cb7b08dd60c9d35efef1"),
- FANMADE_I("sq0", "v1.03", "d2fd6f7404e86182458494e64375e590"),
- FANMADE_I("sq0", "v1.04", "2ad9d1a4624a98571ee77dcc83f231b6"),
- FANMADE_ISVP("sq0", "", "e1a8e4efcce86e1efcaa14633b9eb986", 762, 0x2440, Common::kPlatformCoCo3),
- FANMADE_I("sqx", "v10.0 Feb 05", "c992ae2f8ab18360404efdf16fa9edd1"),
- FANMADE_I("sqx", "v10.0 Jul 18", "812edec45cefad559d190ffde2f9c910"),
- FANMADE_ISVP("sqx", "", "f0a59044475a5fa37c055d8c3eb4d1a7", 768, 0x2440, Common::kPlatformCoCo3),
- FANMADE_F("Space Quest 3.5", "c077bc28d7b36213dd99dc9ecb0147fc", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_F("Space Trek (v1.0)", "807a1aeadb2ace6968831d36ab5ea37a", GF_CLIPCOORDS),
- FANMADE("Special Delivery", "88764dfe61126b8e73612c851b510a33"),
- FANMADE("Speeder Bike Challenge (v1.0)", "2deb25bab379285ca955df398d96c1e7"),
- FANMADE("Star Commander 1 - The Escape (v1.0)", "a7806f01e6fa14ebc029faa58f263750"),
- FANMADE("Star Pilot: Bigger Fish", "8cb26f8e1c045b75c6576c839d4a0172"),
- FANMADE_F("Street Quest (Demo)", "cf2aa94a7eb78dce6892c37f03e310d6", GF_AGIPAL),
- FANMADE("Tales of the Tiki", "8103c9c87e3964690a14a3d0d83f7ddc"),
- FANMADE("Tex McPhilip 1 - Quest For The Papacy", "3c74b9a24b51aa8020ac82bee3132266"),
- FANMADE("Tex McPhilip 2 - Road To Divinity (v1.5)", "7387e8df854440bc26620ca0ea43af9a"),
- FANMADE("Tex McPhilip 3 - A Destiny of Sin (Demo v0.25)", "992d12031a486ad84e592ff5d7c9d782"),
- FANMADE("The 13th Disciple (v1.00)", "887719ad59afce9a41ec057dbb73ad73"),
- FANMADE("The Adventures of a Crazed Hermit", "6e3086cbb794d3299a9c5a9792295511"),
- FANMADE("The Grateful Dead", "c2146631afacf8cb455ce24f3d2d46e7"),
- FANMADE("The Legend of Shay-Larah 1 - The Lost Prince", "04e720c8e30c9cf12db22ea14a24a3dd"),
- FANMADE("The Legend of Zelda: The Fungus of Time (Demo v1.00)", "dcaf8166ceb62a3d9b9aea7f3b197c09"),
- FANMADE("The Legendary Harry Soupsmith (Demo 1998 Apr 2)", "64c46b0d6fc135c9835afa80980d2831"),
- FANMADE("The Legendary Harry Soupsmith (Demo 1998 Aug 19)", "8d06d82970f2c591d880a95476efbcf0"),
- FANMADE("The Long Haired Dude: Encounter of the 18-th Kind", "86ea17b9fc2f3e537a7e40863d352c29"),
- FANMADE("The Lost Planet (v0.9)", "590dffcbd932a9fbe554be13b769cac0"),
- FANMADE("The Lost Planet (v1.0)", "58564df8b6394612dd4b6f5c0fd68d44"),
- FANMADE("The New Adventure of Roger Wilco (v1.00)", "e5f0a7cb8d49f66b89114951888ca688"),
- FANMADE("The Ruby Cast (v0.02)", "ed138e461bb1516e097007e017ab62df"),
- FANMADE("The Shadow Plan", "c02cd10267e721f4e836b1431f504a0a"),
- FANMADE("Time Quest (Demo v0.1)", "12e1a6f03ea4b8c5531acd0400b4ed8d"),
- FANMADE("Time Quest (Demo v0.2)", "7b710608abc99e0861ac59b967bf3f6d"),
- FANMADE_SVP("Time Quest", "90314f473d8317be5cd1f0306f139aea", 300, 0x2440, Common::kPlatformCoCo3),
- FANMADE("Tonight The Shrieking Corpses Bleed (Demo v0.11)", "bcc57a7c8d563fa0c333107ae1c0a6e6"),
- FANMADE("Tonight The Shrieking Corpses Bleed (v1.01)", "36b38f621b38e8d104aa0807302dc8c9"),
- FANMADE("Turks' Quest - Heir to the Planet", "3d19254b737c8b218e5bc4580542b79a"),
- FANMADE("URI Quest (v0.173 Feb 27)", "3986eefcf546dafc45f920ae91a697c3"),
- FANMADE("URI Quest (v0.173 Jan 29)", "494150940d34130605a4f2e67ee40b12"),
- {
- // V - The Graphical Adventure
- {
- "agi-fanmade",
- "V - The Graphical Adventure (Demo 2)",
- AD_ENTRY1s("vdir", "c71f5c1e008d352ae9040b77fcf79327", 3080),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_USEEXTRAASTITLE,
- GUIO_NONE
- },
- GID_FANMADE,
- GType_V3,
- GF_FANMADE,
- 0x3149,
- },
- FANMADE_SVP("V - The Graphical Adventure", "1646eaade74f137a9041eb427a389969", 768, 0x2440, Common::kPlatformCoCo3),
-
- FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Jan 1)", "ae95f0c77d9a97b61420fd192348b937"),
- FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Mar 29)", "11d0417b7b886f963d0b36789dac4c8f"),
- FANMADE("Wizaro (v0.1)", "abeec1eda6eaf8dbc52443ea97ff140c"),
-
- { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
-};
-
-/**
- * The fallback game descriptor used by the AGI engine's fallbackDetector.
- * Contents of this struct are to be overwritten by the fallbackDetector.
- */
-static AGIGameDescription g_fallbackDesc = {
- {
- "",
- "",
- AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
- Common::UNK_LANG,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_FANMADE,
- GType_V2,
- GF_FANMADE,
- 0x2917,
-};
+#include "agi/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -979,11 +145,13 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI
+ Common::GUIO_NOSPEECH,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
-} // End of namespace Agi
-
using namespace Agi;
class AgiMetaEngine : public AdvancedMetaEngine {
@@ -1322,6 +490,9 @@ bool AgiBase::canLoadGameStateCurrently() {
}
bool AgiBase::canSaveGameStateCurrently() {
+ if (getGameID() == GID_BC) // Technically in Black Cauldron we may save anytime
+ return true;
+
return (!(getGameType() == GType_PreAGI) && getflag(fMenusWork) && !_noSaveLoadAllowed && _game.inputEnabled);
}
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
new file mode 100644
index 0000000000..711701f55a
--- /dev/null
+++ b/engines/agi/detection_tables.h
@@ -0,0 +1,863 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+namespace Agi {
+
+using Common::GUIO_NONE;
+
+#define GAME_LVFPN(id,extra,fname,md5,size,lang,ver,features,gid,platform,interp) { \
+ { \
+ id, \
+ extra, \
+ AD_ENTRY1s(fname,md5,size), \
+ lang, \
+ platform, \
+ ADGF_NO_FLAGS, \
+ GUIO_NONE \
+ }, \
+ gid, \
+ interp, \
+ features, \
+ ver, \
+ }
+
+#define GAME_LVFPNF(id,name,fname,md5,size,lang,ver,features,gid,platform,interp) { \
+ { \
+ id, \
+ name, \
+ AD_ENTRY1s(fname,md5,size), \
+ lang, \
+ platform, \
+ ADGF_USEEXTRAASTITLE, \
+ GUIO_NONE \
+ }, \
+ gid, \
+ interp, \
+ features, \
+ ver, \
+ }
+
+#define GAME(id,extra,md5,ver,gid) GAME_LVFPN(id,extra,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V2)
+#define GAME3(id,extra,fname,md5,ver,gid) GAME_LVFPN(id,extra,fname,md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V3)
+
+#define GAME_P(id,extra,md5,ver,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_V2)
+
+#define GAME_FP(id,extra,md5,ver,flags,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V2)
+#define GAME_F(id,extra,md5,ver,flags,gid) GAME_FP(id,extra,md5,ver,flags,gid,Common::kPlatformPC)
+
+#define GAME_PS(id,extra,md5,size,ver,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,size,Common::EN_ANY,ver,0,gid,platform,GType_V2)
+
+#define GAME_LPS(id,extra,md5,size,lang,ver,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,size,lang,ver,0,gid,platform,GType_V2)
+
+#define GAME_LFPS(id,extra,md5,size,lang,ver,flags,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,size,lang,ver,flags,gid,platform,GType_V2)
+
+#define GAME3_P(id,extra,fname,md5,ver,flags,gid,platform) GAME_LVFPN(id,extra,fname,md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
+
+#define GAMEpre_P(id,extra,fname,md5,ver,gid,platform) GAME_LVFPN(id,extra,fname,md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
+
+#define GAMEpre_PS(id,extra,fname,md5,size,ver,gid,platform) GAME_LVFPN(id,extra,fname,md5,size,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
+
+#define GAME3_PS(id,extra,fname,md5,size,ver,flags,gid,platform) GAME_LVFPN(id,extra,fname,md5,size,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
+
+#define FANMADE_ILVF(id,name,md5,lang,ver,features) GAME_LVFPNF(id,name,"logdir",md5,-1,lang,ver,(GF_FANMADE|features),GID_FANMADE,Common::kPlatformPC,GType_V2)
+
+#define FANMADE_ISVP(id,name,md5,size,ver,platform) GAME_LVFPNF(id,name,"logdir",md5,size,Common::EN_ANY,ver,GF_FANMADE,GID_FANMADE,platform,GType_V2)
+#define FANMADE_SVP(name,md5,size,ver,platform) FANMADE_ISVP("agi-fanmade",name,md5,size,ver,platform)
+
+#define FANMADE_LVF(name,md5,lang,ver,features) FANMADE_ILVF("agi-fanmade",name,md5,lang,ver,features)
+
+#define FANMADE_LF(name,md5,lang,features) FANMADE_LVF(name,md5,lang,0x2917,features)
+#define FANMADE_IF(id,name,md5,features) FANMADE_ILVF(id,name,md5,Common::EN_ANY,0x2917,features)
+
+#define FANMADE_V(name,md5,ver) FANMADE_LVF(name,md5,Common::EN_ANY,ver,0)
+#define FANMADE_F(name,md5,features) FANMADE_LF(name,md5,Common::EN_ANY,features)
+#define FANMADE_L(name,md5,lang) FANMADE_LF(name,md5,lang,0)
+#define FANMADE_I(id,name,md5) FANMADE_IF(id,name,md5,0)
+
+#define FANMADE(name,md5) FANMADE_F(name,md5,0)
+
+static const AGIGameDescription gameDescriptions[] = {
+
+ // AGI Demo 1 (PC) 05/87 [AGI 2.425]
+ GAME("agidemo", "Demo 1 1987-05-20", "9c4a5b09cc3564bc48b4766e679ea332", 0x2440, GID_AGIDEMO),
+
+ // AGI Demo 2 (IIgs) 1.0C (Censored)
+ GAME_P("agidemo", "Demo 2 1987-11-24 1.0C", "580ffdc569ff158f56fb92761604f70e", 0x2917, GID_AGIDEMO, Common::kPlatformApple2GS),
+
+ // AGI Demo 2 (PC 3.5") 11/87 [AGI 2.915]
+ GAME("agidemo", "Demo 2 1987-11-24 3.5\"", "e8ebeb0bbe978172fe166f91f51598c7", 0x2917, GID_AGIDEMO),
+
+ // AGI Demo 2 (PC 5.25") 11/87 [v1] [AGI 2.915]
+ GAME("agidemo", "Demo 2 1987-11-24 [version 1] 5.25\"", "852ac303a374df62571642ca1e2d1f0a", 0x2917, GID_AGIDEMO),
+
+ // AGI Demo 2 (PC 5.25") 01/88 [v2] [AGI 2.917]
+ GAME("agidemo", "Demo 2 1987-11-25 [version 2] 5.25\"", "1503f02086ea9f388e7e041c039eaa69", 0x2917, GID_AGIDEMO),
+
+ // AGI Demo 3 (PC) 09/88 [AGI 3.002.102]
+ GAME3("agidemo", "Demo 3 1988-09-13", "dmdir", "289c7a2c881f1d973661e961ced77d74", 0x3149, GID_AGIDEMO),
+
+ // AGI Demo for Kings Quest III and Space Quest I
+ GAME("agidemo", "Demo Kings Quest III and Space Quest I", "502e6bf96827b6c4d3e67c9cdccd1033", 0x2272, GID_AGIDEMO),
+
+ // Black Cauldron (Amiga) 2.00 6/14/87
+ GAME_P("bc", "2.00 1987-06-14", "7b01694af21213b4727bb94476f64eb5", 0x2440, GID_BC, Common::kPlatformAmiga),
+
+ // Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE)
+ // Menus not tested
+ GAME3_P("bc", "1.0O 1989-02-24 (CE)", "bcdir", "dc09d30b147242692f4f85b9811962db", 0x3149, 0, GID_BC, Common::kPlatformApple2GS),
+
+ // Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439]
+ GAME("bc", "2.00 1987-06-14", "7f598d4712319b09d7bd5b3be10a2e4a", 0x2440, GID_BC),
+
+ // Black Cauldron (Russian)
+ GAME_LPS("bc", "", "b7de782dfdf8ea7dde8064f09804bcf5", 357, Common::RU_RUS, 0x2440, GID_BC, Common::kPlatformPC),
+
+ // Black Cauldron (PC 5.25") 2.10 11/10/88 [AGI 3.002.098]
+ GAME3("bc", "2.10 1988-11-10 5.25\"", "bcdir", "0c5a9acbcc7e51127c34818e75806df6", 0x3149, GID_BC),
+
+ // Black Cauldron (PC) 2.10 [AGI 3.002.097]
+ GAME3("bc", "2.10", "bcdir", "0de3953c9225009dc91e5b0d1692967b", 0x3149, GID_BC),
+
+ // Black Cauldron (CoCo3 360k) [AGI 2.023]
+ GAME_PS("bc", "", "51212c54808ade96176f201ae0ac7a6f", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
+
+ // Black Cauldron (CoCo3 360k) [AGI 2.072]
+ GAME_PS("bc", "updated", "c4e1937f74e8100cd0152b904434d8b4", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
+
+// TODO
+// These aren't supposed to work now as they require unsupported agi engine 2.01
+#if 0
+ // Donald Duck's Playground (Amiga) 1.0C
+ // Menus not tested
+ GAME_P("ddp", "1.0C 1987-04-27", "550971d196f65190a5c760d2479406ef", 0x2272, GID_DDP, Common::kPlatformAmiga),
+
+ // Donald Duck's Playground (ST) 1.0A 8/8/86
+ // Menus not tested
+ GAME("ddp", "1.0A 1986-08-08", "64388812e25dbd75f7af1103bc348596", 0x2272, GID_DDP),
+
+ // reported by Filippos (thebluegr) in bugreport #1654500
+ // Menus not tested
+ GAME_PS("ddp", "1.0C 1986-06-09", "550971d196f65190a5c760d2479406ef", 132, 0x2272, GID_DDP, Common::kPlatformPC),
+#endif
+
+ // Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316
+ GAME3_PS("goldrush", "1.01 1989-01-13 aka 2.05 1989-03-09", "dirs", "a1d4de3e75c2688c1e2ca2634ffc3bd8", 2399, 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAmiga),
+
+ // Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88
+ // Menus not tested
+ GAME3_P("goldrush", "1.0M 1989-02-28 (CE) aka 2.01 1988-12-22", "grdir", "3f7b9ce62631434389f85371b11921d6", 0x3149, GF_2GSOLDSOUND, GID_GOLDRUSH, Common::kPlatformApple2GS),
+
+ // Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88
+ GAME3_P("goldrush", "1.01 1989-01-13 aka 2.01 1988-12-22", "grdir", "4dd4d50480a3d6c206fa227ce8142735", 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAtariST),
+
+ // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
+ GAME3("goldrush", "2.01 1988-12-22 5.25\"", "grdir", "db733d199238d4009a9e95f11ece34e9", 0x3149, GID_GOLDRUSH),
+
+ // Gold Rush! (PC 3.5") 2.01 12/22/88 [AGI 3.002.149]
+ GAME3("goldrush", "2.01 1988-12-22 3.5\"", "grdir", "6a285235745f69b4b421403659497216", 0x3149, GID_GOLDRUSH),
+
+ // Gold Rush! (PC 3.5", bought from The Software Farm) 3.0 1998-12-22 [AGI 3.002.149]
+ GAME3("goldrush", "3.0 1998-12-22 3.5\"", "grdir", "6882b6090473209da4cd78bb59f78dbe", 0x3149, GID_GOLDRUSH),
+
+ {
+ // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
+ {
+ "goldrush",
+ "2.01 1988-12-22",
+ {
+ { "grdir", 0, "db733d199238d4009a9e95f11ece34e9", 2399},
+ { "vol.0", 0, "4b6423d143674d3757ab1b875d25951d", 25070},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_GOLDRUSH,
+ GType_V3,
+ GF_MACGOLDRUSH,
+ 0x3149,
+ },
+
+
+ // Gold Rush! (CoCo3 720k) [AGI 2.023]
+ GAME_PS("goldrush", "", "0a41b65efc0cd6c4271e957e6ffbbd8e", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
+
+ // Gold Rush! (CoCo3 360k/720k) [AGI 2.072]
+ GAME_PS("goldrush", "updated", "c49bf56bf91e31a4601a604e51ef8bfb", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
+
+ // King's Quest 1 (Amiga) 1.0U # 2.082
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("kq1", "1.0U 1986", "246c695324f1c514aee2b904fa352fad", 0x2440, GF_MENUS, GID_KQ1, Common::kPlatformAmiga),
+
+ // King's Quest 1 (ST) 1.0V
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("kq1", "1.0V 1986", "c3a017e556c4b0eece366a4cd9abb657", 0x2272, GF_MENUS, GID_KQ1, Common::kPlatformAtariST),
+
+ // King's Quest 1 (IIgs) 1.0S-88223
+ // Menus not tested
+ GAME_P("kq1", "1.0S 1988-02-23", "f4277aa34b43d37382bc424c81627617", 0x2272, GID_KQ1, Common::kPlatformApple2GS),
+
+ // King's Quest 1 (Mac) 2.0C
+ GAME_P("kq1", "2.0C 1987-03-26", "d4c4739d4ac63f7dbd29255425077d48", 0x2440, GID_KQ1, Common::kPlatformMacintosh),
+
+ // King's Quest 1 (PC 5.25"/3.5") 2.0F [AGI 2.917]
+ GAME("kq1", "2.0F 1987-05-05 5.25\"/3.5\"", "10ad66e2ecbd66951534a50aedcd0128", 0x2917, GID_KQ1),
+
+ // King's Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq1", "", "10ad66e2ecbd66951534a50aedcd0128", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
+
+ // King's Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq1", "fixed", "4c8ef8b5d2f1b6c1a93e456d1f1ffc74", 768, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
+
+ // King's Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("kq1", "updated", "94087178c78933a4af3cd24d1c8dd7b2", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
+
+ // King's Quest 2 (IIgs) 2.0A 6/16/88 (CE)
+ GAME_P("kq2", "2.0A 1988-06-16 (CE)", "5203c8b95250a2ecfee93ddb99414753", 0x2917, GID_KQ2, Common::kPlatformApple2GS),
+
+ // King's Quest 2 (Amiga) 2.0J (Broken)
+ GAME_P("kq2", "2.0J 1987-01-29 [OBJECT decrypted]", "b866f0fab2fad91433a637a828cfa410", 0x2440, GID_KQ2, Common::kPlatformAmiga),
+
+ // King's Quest 2 (Mac) 2.0R
+ GAME_P("kq2", "2.0R 1988-03-23", "cbdb0083317c8e7cfb7ac35da4bc7fdc", 0x2440, GID_KQ2, Common::kPlatformMacintosh),
+
+ // King's Quest 2 (PC) 2.1 [AGI 2.411]; entry from DAGII, but missing from Sarien?
+ // XXX: any major differences from 2.411 to 2.440?
+ GAME("kq2", "2.1 1987-04-10", "759e39f891a0e1d86dd29d7de485c6ac", 0x2440, GID_KQ2),
+
+ // King's Quest 2 (PC 5.25"/3.5") 2.2 [AGI 2.426]
+ GAME("kq2", "2.2 1987-05-07 5.25\"/3.5\"", "b944c4ff18fb8867362dc21cc688a283", 0x2917, GID_KQ2),
+
+ // King's Quest 2 (Russian)
+ GAME_LPS("kq2", "", "35211c574ececebdc723b23e35f99275", 543, Common::RU_RUS, 0x2917, GID_KQ2, Common::kPlatformPC),
+
+ // King's Quest 2 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq2", "", "b944c4ff18fb8867362dc21cc688a283", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
+
+ // King's Quest 2 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("kq2", "updated", "f64a606de740a5348f3d125c03e989fe", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
+
+ // King's Quest 2 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq2", "fixed", "fb33ac2768a94a89117a270771db465c", 768, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
+
+ // King's Quest 3 (Amiga) 1.01 11/8/86
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("kq3", "1.01 1986-11-08", "8ab343306df0e2d98f136be4e8cfd0ef", 0x2440, GF_MENUS, GID_KQ3, Common::kPlatformAmiga),
+
+ // King's Quest 3 (ST) 1.02 11/18/86
+ // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
+ GAME_FP("kq3", "1.02 1986-11-18", "8846df2654302b623217ba8bd6d657a9", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformAtariST),
+
+ // King's Quest 3 (Mac) 2.14 3/15/88
+ GAME_P("kq3", "2.14 1988-03-15", "7639c0da5ce94848227d409351fabda2", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
+
+ // King's Quest 3 (IIgs) 2.0A 8/28/88 (CE)
+ GAME_P("kq3", "2.0A 1988-08-28 (CE)", "ac30b7ca5a089b5e642fbcdcbe872c12", 0x2917, GID_KQ3, Common::kPlatformApple2GS),
+
+ // King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333
+ // Original pauses with ESC, has menus accessible with mouse.
+ // ver = 0x3086 -> menus accessible with ESC or mouse, bug #2835581 (KQ3: Game Crash When Leaving Tavern as Fly).
+ // ver = 0x3149 -> menus accessible with mouse, ESC pauses game, bug #2835581 disappears.
+ GAME3_PS("kq3", "2.15 1989-11-15", "dirs", "8e35bded2bc5cf20f5eec2b15523b155", 1805, 0x3149, 0, GID_KQ3, Common::kPlatformAmiga),
+
+ // King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272]
+ // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
+ GAME_FP("kq3", "1.01 1986-11-08", "9c2b34e7ffaa89c8e2ecfeb3695d444b", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
+
+ // King's Quest 3 (Russian)
+ GAME_LFPS("kq3", "", "5856dec6ccb9c4b70aee21044a19270a", 390, Common::RU_RUS, 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
+
+ // King's Quest 3 (PC 5.25") 2.00 5/25/87 [AGI 2.435]
+ GAME("kq3", "2.00 1987-05-25 5.25\"", "18aad8f7acaaff760720c5c6885b6bab", 0x2440, GID_KQ3),
+
+ // King's Quest 3 (Mac) 2.14 3/15/88
+ // Menus not tested
+ GAME_P("kq3", "2.14 1988-03-15 5.25\"", "7650e659c7bc0f1e9f8a410b7a2e9de6", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
+
+ // King's Quest 3 (PC 3.5") 2.14 3/15/88 [AGI 2.936]
+ GAME("kq3", "2.14 1988-03-15 3.5\"", "d3d17b77b3b3cd13246749231d9473cd", 0x2936, GID_KQ3),
+
+ // King's Quest 3 (CoCo3 158k/360k) [AGI 2.023]
+ GAME_PS("kq3", "", "5a6be7d16b1c742c369ef5cc64fefdd2", 429, 0x2440, GID_KQ3, Common::kPlatformCoCo3),
+
+ // King's Quest 4 (PC 5.25") 2.0 7/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.0 1988-07-27", "kq4dir", "f50f7f997208ca0e35b2650baec43a2d", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 3.5") 2.0 7/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.0 1988-07-27 3.5\"", "kq4dir", "fe44655c42f16c6f81046fdf169b6337", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 3.5") 2.2 9/27/88 [AGI 3.002.086]
+ // Menus not tested
+ GAME3("kq4", "2.2 1988-09-27 3.5\"", "kq4dir", "7470b3aeb49d867541fc66cc8454fb7d", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 5.25") 2.3 9/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.3 1988-09-27", "kq4dir", "6d7714b8b61466a5f5981242b993498f", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 3.5") 2.3 9/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.3 1988-09-27 3.5\"", "kq4dir", "82a0d39af891042e99ac1bd6e0b29046", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (IIgs) 1.0K 11/22/88 (CE)
+ // Menus not tested
+ GAME3_P("kq4", "1.0K 1988-11-22", "kq4dir", "8536859331159f15012e35dc82cb154e", 0x3086, 0, GID_KQ4, Common::kPlatformApple2GS),
+
+ // King's Quest 4 demo (PC) [AGI 3.002.102]
+ // Menus not tested
+ GAME3("kq4", "Demo 1988-12-20", "dmdir", "a3332d70170a878469d870b14863d0bf", 0x3149, GID_KQ4),
+
+ // King's Quest 4 (CoCo3 720k) [AGI 2.023]
+ GAME_PS("kq4", "", "9e7729a28e749ca241d2bf71b9b2dbde", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
+
+ // King's Quest 4 (CoCo3 360k/720k) [AGI 2.072]
+ GAME_PS("kq4", "updated", "1959ca10739edb34069bb504dbd74805", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
+
+ // Leisure Suit Larry 1 (PC 5.25"/3.5") 1.00 6/1/87 [AGI 2.440]
+ GAME("lsl1", "1.00 1987-06-01 5.25\"/3.5\"", "1fe764e66857e7f305a5f03ca3f4971d", 0x2440, GID_LSL1),
+
+ // Leisure Suit Larry 1 Polish
+ GAME_LPS("lsl1", "2.00 2001-12-11", "7ba1fccc46d27c141e704706c1d0a85f", 303, Common::PL_POL, 0x2440, GID_LSL1, Common::kPlatformPC),
+
+ // Leisure Suit Larry 1 Polish - Demo
+ GAME_LPS("lsl1", "Demo", "3b2f564306c401dff6334441df967ddd", 666, Common::PL_POL, 0x2917, GID_LSL1, Common::kPlatformPC),
+
+ // Leisure Suit Larry 1 (ST) 1.04 6/18/87
+ GAME_P("lsl1", "1.04 1987-06-18", "8b579f8673fe9448c2538f5ed9887cf0", 0x2440, GID_LSL1, Common::kPlatformAtariST),
+
+ // Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy
+ GAME_P("lsl1", "1.05 1987-06-26", "3f5d26d8834ca49c147fb60936869d56", 0x2440, GID_LSL1, Common::kPlatformAmiga),
+
+ // Leisure Suit Larry 1 (IIgs) 1.0E
+ GAME_P("lsl1", "1.0E 1987", "5f9e1dd68d626c6d303131c119582ad4", 0x2440, GID_LSL1, Common::kPlatformApple2GS),
+
+ // Leisure Suit Larry 1 (Mac) 1.05 6/26/87
+ GAME_P("lsl1", "1.05 1987-06-26", "8a0076429890531832f0dc113285e31e", 0x2440, GID_LSL1, Common::kPlatformMacintosh),
+
+ // Leisure Suit Larry 1 (CoCo3 158k/360k) [AGI 2.072]
+ GAME_PS("lsl1", "", "a2de1fe76565c3e8b40c9d036b5e5612", 198, 0x2440, GID_LSL1, Common::kPlatformCoCo3),
+
+ // Manhunter NY (ST) 1.03 10/20/88
+ GAME3_P("mh1", "1.03 1988-10-20", "mhdir", "f2d58056ad802452d60776ee920a52a6", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
+
+ // Manhunter NY (IIgs) 2.0E 10/05/88 (CE)
+ GAME3_P("mh1", "2.0E 1988-10-05 (CE)", "mhdir", "2f1509f76f24e6e7d213f2dadebbf156", 0x3149, 0, GID_MH1, Common::kPlatformApple2GS),
+
+ // Manhunter NY (Amiga) 1.06 3/18/89
+ GAME3_P("mh1", "1.06 1989-03-18", "dirs", "92c6183042d1c2bb76236236a7d7a847", 0x3149, GF_OLDAMIGAV20, GID_MH1, Common::kPlatformAmiga),
+
+ // reported by Filippos (thebluegr) in bugreport #1654500
+ // Manhunter NY (PC 5.25") 1.22 8/31/88 [AGI 3.002.107]
+ GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "0c7b86f05fe02c2e26cff1b07450b82a", 2123, 0x3149, 0, GID_MH1, Common::kPlatformPC),
+
+ // Manhunter NY (PC 3.5") 1.22 8/31/88 [AGI 3.002.102]
+ GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "5b625329021ad49fd0c1d6f2d6f54bba", 2141, 0x3149, 0, GID_MH1, Common::kPlatformPC),
+
+ // Manhunter NY (CoCo3 720k) [AGI 2.023]
+ GAME_PS("mh1", "", "b968285caf2f591c78dd9c9e26ab8974", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
+
+ // Manhunter NY (CoCo3 360k/720k) [AGI 2.072]
+ GAME_PS("mh1", "updated", "d47da950c62289f8d4ccf36af73365f2", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
+
+ // Manhunter SF (ST) 1.0 7/29/89
+ GAME3_P("mh2", "1.0 1989-07-29", "mh2dir", "5e3581495708b952fea24438a6c7e040", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
+
+ // Manhunter SF (Amiga) 3.06 8/17/89 # 2.333
+ GAME3_PS("mh2", "3.06 1989-08-17", "dirs", "b412e8a126368b76696696f7632d4c16", 2573, 0x3086, GF_OLDAMIGAV20, GID_MH2, Common::kPlatformAmiga),
+
+ // Manhunter SF (PC 5.25") 3.03 8/17/89 [AGI 3.002.149]
+ GAME3("mh2", "3.03 1989-08-17 5.25\"", "mh2dir", "b90e4795413c43de469a715fb3c1fa93", 0x3149, GID_MH2),
+
+ // Manhunter SF (PC 3.5") 3.02 7/26/89 [AGI 3.002.149]
+ GAME3("mh2", "3.02 1989-07-26 3.5\"", "mh2dir", "6fb6f0ee2437704c409cf17e081ba152", 0x3149, GID_MH2),
+
+ // Manhunter SF (CoCo3 720k) [AGI 2.023]
+ GAME_PS("mh2", "", "acaaa577e10d1753c5a74f6ae1d858d4", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
+
+ // Manhunter SF (CoCo3 720k) [AGI 2.072]
+ GAME_PS("mh2", "updated", "c64875766700196e72a92359f70f45a9", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
+
+ // Mickey's Space Adventure
+ // Preagi game
+ GAMEpre_P("mickey", "", "1.pic", "b6ec04c91a05df374792872c4d4ce66d", 0x0000, GID_MICKEY, Common::kPlatformPC),
+
+#if 0
+ // Mixed-Up Mother Goose (Amiga) 1.1
+ // Problematic: crashes
+ // Menus not tested
+ GAME3_PS("mixedup", "1.1 1986-12-10", "dirs", "5c1295fe6daaf95831195ba12894dbd9", 2021, 0x3086, 0, GID_MIXEDUP, Common::kPlatformAmiga),
+#endif
+
+ // Mixed Up Mother Goose (IIgs)
+ GAME_P("mixedup", "1987", "3541954a7303467c6df87665312ffb6a", 0x2917, GID_MIXEDUP, Common::kPlatformApple2GS),
+
+ // Mixed-Up Mother Goose (PC) [AGI 2.915]
+ GAME("mixedup", "1987-11-10", "e524655abf9b96a3b179ffcd1d0f79af", 0x2917, GID_MIXEDUP),
+
+ // Mixed-Up Mother Goose (CoCo3 360k) [AGI 2.072]
+ GAME_PS("mixedup", "", "44e63e9b4d4822a31edea0e8a7e7eac4", 606, 0x2440, GID_MIXEDUP, Common::kPlatformCoCo3),
+
+ // Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915]
+ GAME("pq1", "2.0E 1987-11-17", "2fd992a92df6ab0461d5a2cd83c72139", 0x2917, GID_PQ1),
+
+ // Police Quest 1 (Mac) 2.0G 12/3/87
+ GAME_P("pq1", "2.0G 1987-12-03", "805750b66c1c5b88a214e67bfdca17a1", 0x2440, GID_PQ1, Common::kPlatformMacintosh),
+
+ // Police Quest 1 (IIgs) 2.0B-88421
+ GAME_P("pq1", "2.0B 1988-04-21", "e7c175918372336461e3811d594f482f", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
+
+ // Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310
+ GAME3_PS("pq1", "2.0B 1989-02-22", "dirs", "cfa93e5f2aa7378bddd10ad6746a2ffb", 1613, 0x3149, 0, GID_PQ1, Common::kPlatformAmiga),
+
+ // Police Quest 1 (IIgs) 2.0A-88318
+ GAME_P("pq1", "2.0A 1988-03-18", "8994e39d0901de3d07cecfb954075bb5", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
+
+ // Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911]
+ GAME("pq1", "2.0A 1987-10-23", "b9dbb305092851da5e34d6a9f00240b1", 0x2917, GID_PQ1),
+
+ // Police Quest 1 (Russian)
+ GAME_LPS("pq1", "", "604cc8041d24c4c7e5fa8baf386ef76e", 360, Common::RU_RUS, 0x2917, GID_PQ1, Common::kPlatformPC),
+
+ // Police Quest 1 2.0G 12/3/87
+ GAME("pq1", "2.0G 1987-12-03 5.25\"/ST", "231f3e28170d6e982fc0ced4c98c5c1c", 0x2440, GID_PQ1),
+
+ // Police Quest 1 (PC) 2.0G 12/3/87; entry from DAGII, but missing from Sarien?
+ // not sure about disk format -- dsymonds
+ GAME("pq1", "2.0G 1987-12-03", "d194e5d88363095f55d5096b8e32fbbb", 0x2917, GID_PQ1),
+
+ // Police Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("pq1", "", "28a077041f75aab78f66804800940085", 375, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
+
+ // Police Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("pq1", "updated", "63b9a9c6eec154751dd446cd3693e0e2", 768, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 1 (ST) 1.1A
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.1A 1986-02-06", "6421fb64b0e6604c9dd065975d9279e9", 0x2440, GF_MENUS, GID_SQ1, Common::kPlatformAtariST),
+
+ // Space Quest 1 (PC 360k) 1.1A [AGI 2.272]
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.1A 1986-11-13", "8d8c20ab9f4b6e4817698637174a1cb6", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (PC 720k) 1.1A [AGI 2.272]
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.1A 720kb", "0a92b1be7daf3bb98caad3f849868aeb", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (Amiga) 1.2 # 2.082
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.2 1986", "0b216d931e95750f1f4837d6a4b821e5", 0x2440, GF_MENUS | GF_OLDAMIGAV20, GID_SQ1, Common::kPlatformAmiga),
+
+ // Space Quest 1 (Mac) 1.5D
+ GAME_P("sq1", "1.5D 1987-04-02", "ce88419aadd073d1c6682d859b3d8aa2", 0x2440, GID_SQ1, Common::kPlatformMacintosh),
+
+ // Space Quest 1 (IIgs) 2.2
+ GAME_P("sq1", "2.2 1987", "64b9b3d04c1066d36e6a6e56187a83f7", 0x2917, GID_SQ1, Common::kPlatformApple2GS),
+
+ // Space Quest 1 (PC) 1.0X [AGI 2.089]
+ // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
+ GAME_FP("sq1", "1.0X 1986-09-24", "af93941b6c51460790a9efa0e8cb7122", 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (Russian)
+ GAME_LFPS("sq1", "", "a279eb8ddbdefdb1ea6adc827a1d632a", 372, Common::RU_RUS, 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (PC 5.25"/3.5") 2.2 [AGI 2.426/2.917]
+ GAME("sq1", "2.2 1987-05-07 5.25\"/3.5\"", "5d67630aba008ec5f7f9a6d0a00582f4", 0x2440, GID_SQ1),
+
+ // Space Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("sq1", "", "5d67630aba008ec5f7f9a6d0a00582f4", 372, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("sq1", "fixed", "ca822b768b6462e410423ea7f498daee", 768, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("sq1", "updated", "7fa54e6bb7ffeb4cf20eca39d86f5fb2", 387, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 2 (PC 3.5") 2.0D [AGI 2.936]
+ GAME("sq2", "2.0D 1988-03-14 3.5\"", "85390bde8958c39830e1adbe9fff87f3", 0x2936, GID_SQ2),
+
+ // Space Quest 2 (IIgs) 2.0A 7/25/88 (CE)
+ GAME_P("sq2", "2.0A 1988-07-25 (CE)", "5dfdac98dd3c01fcfb166529f917e911", 0x2936, GID_SQ2, Common::kPlatformApple2GS),
+
+ {
+ // Space Quest 2 (Amiga) 2.0F
+ {
+ "sq2",
+ "2.0F 1986-12-09 [VOL.2->PICTURE.16 broken]",
+ {
+ { "logdir", 0, "28add5125484302d213911df60d2aded", 426},
+ { "object", 0, "5dc52be721257719f4b311a84ce22b16", 372},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_SQ2,
+ GType_V2,
+ 0,
+ 0x2936,
+ },
+
+
+ // Space Quest 2 (Mac) 2.0D
+ GAME_P("sq2", "2.0D 1988-04-04", "bfbebe0b59d83f931f2e1c62ce9484a7", 0x2936, GID_SQ2, Common::kPlatformMacintosh),
+
+ // reported by Filippos (thebluegr) in bugreport #1654500
+ // Space Quest 2 (PC 5.25") 2.0A [AGI 2.912]
+ GAME_PS("sq2", "2.0A 1987-11-06 5.25\"", "ad7ce8f800581ecc536f3e8021d7a74d", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
+
+ // Space Quest 2 (Russian)
+ GAME_LPS("sq2", "", "ba21c8934caf28e3ba45ce7d1cd6b041", 423, Common::RU_RUS, 0x2917, GID_SQ2, Common::kPlatformPC),
+
+ // Space Quest 2 (PC 3.5") 2.0A [AGI 2.912]
+ GAME_PS("sq2", "2.0A 1987-11-06 3.5\"", "6c25e33d23b8bed42a5c7fa63d588e5c", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
+
+ // Space Quest 2 (PC 5.25"/ST) 2.0C/A [AGI 2.915]
+ // Menus not tested
+ GAME("sq2", "2.0C/A 5.25\"/ST", "bd71fe54869e86945041700f1804a651", 0x2917, GID_SQ2),
+
+ // Space Quest 2 (PC 3.5") 2.0F [AGI 2.936]
+ GAME("sq2", "2.0F 1989-01-05 3.5\"", "28add5125484302d213911df60d2aded", 0x2936, GID_SQ2),
+
+ // Space Quest 2 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("sq2", "", "12973d39b892dc9d280257fd271e9597", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
+
+ // Space Quest 2 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("sq2", "updated", "d24f19b047e65e1763eff4b46f3d50df", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
+
+ // Troll's Tale
+ GAMEpre_PS("troll", "", "troll.img", "62903f264b3d849be4214b3a5c42a2fa", 184320, 0x0000, GID_TROLL, Common::kPlatformPC),
+
+ // Winnie the Pooh in the Hundred Acre Wood
+ GAMEpre_P("winnie", "", "title.pic", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformPC),
+
+ // Winnie the Pooh in the Hundred Acre Wood (Amiga)
+ GAMEpre_P("winnie", "", "title", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformAmiga),
+
+ // Winnie the Pooh in the Hundred Acre Wood (C64)
+ GAMEpre_P("winnie", "", "title.pic", "d4eb97cffc866110f71e1ec9f84fe643", 0x0000, GID_WINNIE, Common::kPlatformC64),
+
+ // Winnie the Pooh in the Hundred Acre Wood (Apple //gs)
+ GAMEpre_P("winnie", "", "title.pic", "45e06010a3c61d78f4661103c901ae11", 0x0000, GID_WINNIE, Common::kPlatformApple2GS),
+
+ // Xmas Card 1986 (PC) [AGI 2.272]
+ GAME("xmascard", "1986-11-13 [version 1]", "3067b8d5957e2861e069c3c0011bd43d", 0x2272, GID_XMASCARD),
+
+ // Xmas Card 1986 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("xmascard", "", "25ad35e9628fc77e5e0dd35852a272b6", 768, 0x2440, GID_XMASCARD, Common::kPlatformCoCo3),
+
+ FANMADE_F("2 Player Demo", "4279f46b3cebd855132496476b1d2cca", GF_AGIMOUSE),
+ FANMADE("AGI Contest 1 Template", "d879aed25da6fc655564b29567358ae2"),
+ FANMADE("AGI Contest 2 Template", "5a2fb2894207eff36c72f5c1b08bcc07"),
+ FANMADE("AGI Mouse Demo 0.60 demo 1", "c07e2519de674c67386cb2cc6f2e3904"),
+ FANMADE("AGI Mouse Demo 0.60 demo 2", "cc49d8b88ed6faf4f53ce92c84e0fe1b"),
+ FANMADE("AGI Mouse Demo 0.70", "3497c291e4afb6f758e61740678a2aec"),
+ FANMADE_F("AGI Mouse Demo 1.00", "20397f0bf0ef936f416bb321fb768fc7", GF_AGIMOUSE),
+ FANMADE_F("AGI Mouse Demo 1.10", "f4ad396b496d6167635ad0b410312ab8", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("AGI Piano (v1.0)", "8778b3d89eb93c1d50a70ef06ef10310"),
+ FANMADE("AGI Quest (v1.46-TJ0)", "1cf1a5307c1a0a405f5039354f679814"),
+ GAME("tetris", "", "7a874e2db2162e7a4ce31c9130248d8a", 0x2917, GID_FANMADE),
+ FANMADE_V("AGI Trek (Demo)", "c02882b8a8245b629c91caf7eb78eafe", 0x2440),
+ FANMADE_F("AGI256 Demo", "79261ac143b2e2773b2753674733b0d5", GF_AGI256),
+ FANMADE_F("AGI256-2 Demo", "3cad9b3aff1467cebf0c5c5b110985c5", GF_AGI256_2),
+ FANMADE_LF("Abrah: L'orphelin de l'espace (v1.2)", "b7b6d1539e14d5a26fa3088288e1badc", Common::FR_FRA, GF_AGIPAL),
+ FANMADE("Acidopolis", "7017db1a4b726d0d59e65e9020f7d9f7"),
+ FANMADE("Agent 0055 (v1.0)", "c2b34a0c77acb05482781dda32895f24"),
+ FANMADE("Agent 06 vs. The Super Nazi", "136f89ca9f117c617e88a85119777529"),
+ FANMADE("Agent Quest", "59e49e8f72058a33c00d60ee1097e631"),
+ FANMADE("Al Pond - On Holiday (v1.0)", "a84975496b42d485920e886e92eed68b"),
+ FANMADE("Al Pond - On Holiday (v1.1)", "7c95ac4689d0c3bfec61e935f3093634"),
+ FANMADE("Al Pond - On Holiday (v1.3)", "8f30c260de9e1dd3d8b8f89cc19d2633"),
+ FANMADE("Al Pond 1 - Al Lives Forever (v1.0)", "e8921c3043b749b056ff51f56d1b451b"),
+ FANMADE("Al Pond 1 - Al Lives Forever (v1.3)", "fb4699474054962e0dbfb4cf12ca52f6"),
+ FANMADE("Apocalyptic Quest (v0.03 Teaser)", "42ced528b67965d3bc3b52c635f94a57"),
+ FANMADE_F("Apocalyptic Quest (v4.00 Alpha 1)", "e15581628d84949b8d352d224ec3184b", GF_AGIMOUSE),
+ FANMADE_F("Apocalyptic Quest (v4.00 Alpha 2)", "0eee850005860e46345b38fea093d194", GF_AGIMOUSE),
+ FANMADE_F("Band Quest (Demo)", "7326abefd793571cc17ed0db647bdf34", GF_AGIMOUSE),
+ FANMADE_F("Band Quest (Early Demo)", "de4758dd34676b248c8301b32d93bc6f", GF_AGIMOUSE),
+ FANMADE("Beyond the Titanic 2", "9b8de38dc64ffb3f52b7877ea3ebcef9"),
+ FANMADE("Biri Quest 1", "1b08f34f2c43e626c775c9d6649e2f17"),
+ FANMADE("Bob The Farmboy", "e4b7df9d0830addee5af946d380e66d7"),
+ FANMADE_F("Boring Man 1: The Toad to Robinland", "d74481cbd227f67ace37ce6a5493039f", GF_AGIMOUSE),
+ FANMADE_F("Boring Man 2: Ho Man! This Game Sucks!", "250032ba105bdf7c1bc4fed767c2d37e", GF_AGIMOUSE),
+ FANMADE("Botz", "a8fabe4e807adfe5ec02bfec6d983695"),
+ FANMADE("Brian's Quest (v1.0)", "0964aa79b9cdcff7f33a12b1d7e04b9c"),
+ FANMADE("CPU-21 (v1.0)", "35b7cdb4d17e890e4c52018d96e9cbf4"),
+ GAME("caitlyn", "Demo", "5b8a3cdb2fc05469f8119d49f50fbe98", 0x2917, GID_FANMADE),
+ GAME("caitlyn", "", "818469c484cae6dad6f0e9a353f68bf8", 0x2917, GID_FANMADE),
+ FANMADE("Car Driver (v1.1)", "2311611d2d36d20ccc9da806e6cba157"),
+ FANMADE("Cloak of Darkness (v1.0)", "5ba6e18bf0b53be10db8f2f3831ee3e5"),
+ FANMADE("Coco Coq (English) - Coco Coq In Grostesteing's Base (v.1.0.3)", "97631f8e710544a58bd6da9e780f9320"),
+ FANMADE_L("Coco Coq (French) - Coco Coq Dans la Base de Grostesteing (v1.0.2)", "ef579ebccfe5e356f9a557eb3b2d8649", Common::FR_FRA),
+ FANMADE("Corby's Murder Mystery (v1.0)", "4ebe62ac24c5a8c7b7898c8eb070efe5"),
+ FANMADE_F("DG: The AGIMouse Adventure (English v1.1)", "efe453b92bc1487ea69fbebede4d5f26", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_LF("DG: The AGIMouse Adventure (French v1.1)", "eb3d17ca466d672cbb95947e8d6e846a", Common::FR_FRA, GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("DG: The Adventure Game (English v1.1)", "0d6376d493fa7a21ec4da1a063e12b25"),
+ FANMADE_L("DG: The Adventure Game (French v1.1)", "258bdb3bb8e61c92b71f2f456cc69e23", Common::FR_FRA),
+ FANMADE("Dashiki (16 Colors)", "9b2c7b9b0283ab9f12bedc0cb6770a07"),
+ FANMADE_F("Dashiki (256 Colors)", "c68052bb209e23b39b55ff3d759958e6", GF_AGIMOUSE|GF_AGI256),
+ FANMADE("Date Quest 1 (v1.0)", "ba3dcb2600645be53a13170aa1a12e69"),
+ FANMADE("Date Quest 2 (v1.0 Demo)", "1602d6a2874856e928d9a8c8d2d166e9"),
+ FANMADE("Date Quest 2 (v1.0)", "f13f6fc85aa3e6e02b0c20408fb63b47"),
+ FANMADE("Dave's Quest (v0.07)", "f29c3660de37bacc1d23547a167f27c9"),
+ FANMADE("Dave's Quest (v0.17)", "da3772624cc4a86f7137db812f6d7c39"),
+ FANMADE("Disco Nights (Demo)", "dc5a2b21182ba38bdcd992a3a978e690"),
+ FANMADE("Dogs Quest - The Quest for the Golden Bone (v1.0)", "f197357edaaea0ff70880602d2f09b3e"),
+ FANMADE("Dr. Jummybummy's Space Adventure", "988bd81785f8a452440a2a8ac67f96aa"),
+ FANMADE("Ed Ward", "98be839b9f30cbedea4c9cee5442d827"),
+ FANMADE("Elfintard", "c3b847e9e9e978af9708df76a0751dc2"),
+ FANMADE("Enclosure (v1.01)", "f08e66fee9ecdde77db7ee9a10c96ba2"),
+ FANMADE("Enclosure (v1.03)", "e4a0613ed02401502e506ba3565a8c40"),
+ FANMADE_SVP("Enclosure", "fe98e6126db74c6cc6fd8fe395cc6e8c", 345, 0x2440, Common::kPlatformCoCo3),
+ FANMADE("Epic Fighting (v0.1)", "aff24a1b3bdd676187685c4d95ba4294"),
+ FANMADE("Escape Quest (v0.0.3)", "2346b65619b1da0298b715b06d1a45a1"),
+ FANMADE("Escape from the Desert (beta 1)", "dfdc634d340854bd6ece28024010758d"),
+ FANMADE("Escape from the Salesman", "e723ca4fe0f6f56affe039fbb4dbeb6c"),
+ FANMADE("Fu$k Quest 1 (final)", "1cd0587422313f6ca77d6a95988e88ed"),
+ FANMADE("Fu$k Quest 1", "1cd0587422313f6ca77d6a95988e88ed"),
+ FANMADE("Fu$k Quest 2 - Romancing the Bone (Teaser)", "d288355d71d9bb1639260ccaa3b2fbfe"),
+ FANMADE("Fu$k Quest 2 - Romancing the Bone", "294beeb7765c7ea6b05ed7b9bf7bff4f"),
+ FANMADE("Gennadi Tahab Autot - Mission Pack 1 - Kuressaare", "bfa5fe71978e6ccf3d4eedd430124015"),
+ FANMADE("Go West, Young Hippie", "ff31484ea465441cb5f3a0f8e956b716"),
+ FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"),
+
+ {
+ // Groza
+ {
+ "agi-fanmade",
+ "Groza (russian) [AGDS sample]",
+ AD_ENTRY1("logdir", "421da3a18004122a966d64ab6bd86d2e"),
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ ADGF_USEEXTRAASTITLE,
+ GUIO_NONE
+ },
+ GID_FANMADE,
+ GType_V2,
+ GF_AGDS,
+ 0x2440,
+ },
+
+ {
+ // Get Outta SQ
+ {
+ "agi-fanmade",
+ "Get Outta Space Quest",
+ AD_ENTRY1("logdir", "aaea5b4a348acb669d13b0e6f22d4dc9"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_USEEXTRAASTITLE,
+ GUIO_NONE
+ },
+ GID_GETOUTTASQ,
+ GType_V2,
+ 0,
+ 0x2440,
+ },
+
+ FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE),
+ FANMADE("Hank's Quest (v1.0 English) - Victim of Society", "64c15b3d0483d17888129100dc5af213"),
+ FANMADE("Hank's Quest (v1.1 English) - Victim of Society", "86d1f1dd9b0c4858d096e2a60cca8a14"),
+ FANMADE_L("Hank's Quest (v1.81 Dutch) - Slachtoffer Van Het Gebeuren", "41e53972d55ff3dff9e90d15fe1b659f", Common::NL_NLD),
+ FANMADE("Hank's Quest (v1.81 English) - Victim of Society", "7a776383282f62a57c3a960dafca62d1"),
+ FANMADE("Herbao (v0.2)", "6a5186fc8383a9060517403e85214fc2"),
+ FANMADE_F("Hitler's Legacy (v.0004q)", "a412881269ba34584bd0a3268e5a9863", GF_AGIMOUSE),
+ FANMADE("Hobbits", "4a1c1ef3a7901baf0ab45fde0cfadd89"),
+ FANMADE_F("Isabella Coq - A Present For My Dad", "55c6819f2330c4d5d6459874c9f123d9", GF_AGIMOUSE),
+ FANMADE("Jack & Julia - VAMPYR", "8aa0b9a26f8d5a4421067ab8cc3706f6"),
+ FANMADE("Jeff's Quest (v.5 alpha Jun 1)", "10f1720eed40c12b02a0f32df3e72ded"),
+ FANMADE("Jeff's Quest (v.5 alpha May 31)", "51ff71c0ed90db4e987a488ed3bf0551"),
+ FANMADE("Jen's Quest (Demo 1)", "361afb5bdb6160213a1857245e711939"),
+ FANMADE("Jen's Quest (Demo 2)", "3c321eee33013b289ab8775449df7df2"),
+ FANMADE("Jiggy Jiggy Uh! Uh!", "bc331588a71e7a1c8840f6cc9b9487e4"),
+ FANMADE("Jimmy In: The Alien Attack (v0.1)", "a4e9db0564a494728de7873684a4307c"),
+ FANMADE("Joe McMuffin In \"What's Cooking, Doc\" (v1.0)", "8a3de7e61a99cb605fa6d233dd91c8e1"),
+ FANMADE_LVF("Jolimie, le Village Maudit (v0.5)", "21818501636b3cb8ad5de5c1a66de5c2", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_LVF("Jolimie, le Village Maudit (v1.1)", "68d7aef1161bb5972fe03efdf29ccb7f", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("Journey Of Chef", "aa0a0b5a6364801ae65fdb96d6741df5"),
+ FANMADE("Jukebox (v1.0)", "c4b9c5528cc67f6ba777033830de7751"),
+ FANMADE("Justin Quest (v1.0 in development)", "103050989da7e0ffdc1c5e1793a4e1ec"),
+ FANMADE("J\xf5ulumaa (v0.05) (Estonian)", "53982ecbfb907e41392b3961ad1c3475"),
+ FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Mar 26)", "a25d7379d281b1b296d4785df90a8e78"),
+ FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Aug 16)", "6b4f796d0421d2e12e501b511962e03a"),
+ FANMADE("Lasse Holm: The Quest for Revenge (v1.0)", "f9fbcc8a4ef510bfbb92423296ff4abb"),
+ FANMADE("Lawman for Hire", "c78b28bfd3767dd455b992cd8b7854fa"),
+ FANMADE("Lefty Goes on Vacation (Not in The Right Place)", "ccdc49a33870310b01f2c48b8a1f3c34"),
+ FANMADE("Les Ins\xe3parables (v1.0)", "4b780887cab0ecabc5eca319acb3acf2"),
+ FANMADE("Little Pirate (Demo 2 v0.6)", "437068efe4ec32d436da09d6f2ea56e1"),
+ FANMADE("Lost Eternity (v1.0)", "95f15c5632feb8a39e9ca3d9af35fcc9"),
+ FANMADE("MD Quest - The Search for Michiel (v0.10)", "2a6fcb21d2b5e4144c38ed817fabe8ee"),
+ FANMADE("Maale Adummin Quest", "ddfbeb33feb7cf78504fe4dba14ec63b"),
+ FANMADE("Monkey Man", "2322d03f997e8cc235d4578efff69cfa"),
+ FANMADE_F("Napalm Quest (v0.5)", "b659afb491d967bb34810d1c6ce22093", GF_AGIMOUSE),
+ FANMADE("Naturette 1 (English v1.2)", "0a75884e7f010974a230bdf269651117"),
+ FANMADE("Naturette 1 (English v1.3)", "f15bbf999ac55ebd404aa1eb84f7c1d9"),
+ FANMADE_L("Naturette 1 (French v1.2)", "d3665622cc41aeb9c7ecf4fa43f20e53", Common::FR_FRA),
+ FANMADE_F("Naturette 2: Daughter of the Moon (v1.0)", "bdf76a45621c7f56d1c9d40292c6137a", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_F("Naturette 3: Adventure in Treeworld (v1.0a)", "6dbb0e7fc75fec442e6d9e5a06f1530e", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_F("Naturette 4: From a Planet to Another Planet (Not Finished)", "13be8cd9cf35aeff0a39b8757057fbc8", GF_AGIMOUSE),
+ // FIXME: Actually Naturette 4 has both English and French language support built into it. How to add that information?
+ FANMADE_F("Naturette 4: From a Planet to Another Planet (2007-10-05)", "8253706b6ef5423a79413b216760297c", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("New AGI Hangman Test", "d69c0e9050ccc29fd662b74d9fc73a15"),
+ FANMADE("Nick's Quest - In Pursuit of QuakeMovie (v2.1 Gold)", "e29cbf9222551aee40397fabc83eeca0"),
+ FANMADE_F("Open Mic Night (v0.1)", "70000a2f67aac27d1133d019df70246d", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("Operation: Recon", "0679ce8405411866ccffc8a6743370d0"),
+ FANMADE("Patrick's Quest (Demo v1.0)", "f254f5b894b98fec5f92acc07fb62841"),
+ FANMADE("Phantasmagoria", "87d20c1c11aee99a4baad3797b63146b"),
+ FANMADE("Pharaoh Quest (v0.0)", "51c630899d076cf799e573dadaa2276d"),
+ FANMADE("Phil's Quest - the Search for Tolbaga", "5e7ca45c360e03164b8358e49900c588"),
+ FANMADE("Pinkun Maze Quest (v0.1)", "148ff0843af389928b3939f463bfd20d"),
+ FANMADE("Pirate Quest", "bb612a919ed2b9ea23bbf03ce69fed42"),
+ FANMADE("Pothead (v0.1)", "d181101385d3a45082f418cd4b3c5b01"),
+ FANMADE("President's Quest", "4937d0e8ecadb7888faeb347799b0388"),
+ FANMADE("Prince Quest", "266248d75c3130c8ccc9c9bf2ad30a0d"),
+ FANMADE("Professor (English) - The Professor is Missing (Mar 17)", "6232de31cc204affdf2e92dfe3dc0e4d"),
+ FANMADE("Professor (English) - The Professor is Missing (Mar 22)", "b5fcf0ca2f0d1c073be82f01e2170961"),
+ FANMADE_L("Professor (French) - Le Professeur a Disparu", "7d9f8a4d4610bb9b0b97caa17590c2d3", Common::FR_FRA),
+ FANMADE("Quest for Glory VI - Hero's Adventure", "d26765c3075064c80d284c5e06e33a7e"),
+ FANMADE("Quest for Home", "d2895dc1cd3930f2489af0f843b144b3"),
+ FANMADE("Quest for Ladies (demo v1.1 Apr 1)", "3f6e02f16e1154a0daf296c8895edd97"),
+ FANMADE("Quest for Ladies (demo v1.1 Apr 6)", "f75e7b6a0769a3fa926eea0854711591"),
+ FANMADE("Quest for Piracy 1 - Enter the Silver Pirate (v0.15)", "d23f5c2a26f6dc60c686f8a2436ea4a6"),
+ FANMADE("Quest for a Record Deal", "f4fbd7abf056d2d3204f790da5ac89ab"),
+ FANMADE("Ralph's Quest (v0.1)", "5cf56378aa01a26ec30f25295f0750ca"),
+ FANMADE("Residence 44 Quest (Dutch v0.99)", "7c5cc64200660c70240053b33d379d7d"),
+ FANMADE("Residence 44 Quest (English v0.99)", "fe507851fddc863d540f2bec67cc67fd"),
+ FANMADE("Residence 44 Quest (English v1.0a)", "f99e3f69dc8c77a45399da9472ef5801"),
+ FANMADE("SQ2Eye (v0.3)", "2be2519401d38ad9ce8f43b948d093a3"),
+ // FANMADE("SQ2Eye (v0.4)", "2be2519401d38ad9ce8f43b948d093a3"),
+ FANMADE("SQ2Eye (v0.41)", "f0e82c55f10eb3542d7cd96c107ae113"),
+ FANMADE("SQ2Eye (v0.42)", "d7beae55f6328ef8b2da47b1aafea40c"),
+ FANMADE("SQ2Eye (v0.43)", "2a895f06e45de153bb4b77c982009e06"),
+ FANMADE("SQ2Eye (v0.44)", "5174fc4b6d8a477ba0ff0575cd64e0aa"),
+ FANMADE("SQ2Eye (v0.45)", "6e06f8bb7b90ce6f6aabf1a0e620159c"),
+ FANMADE("SQ2Eye (v0.46)", "bf0ad7a035ff9113951d09d1efe380c4"),
+ FANMADE("SQ2Eye (v0.47)", "85dc3be1d33ff932c292b74f9037abaa"),
+ FANMADE("SQ2Eye (v0.48)", "587574252972a5b5c070a647973a9b4a"),
+ FANMADE("SQ2Eye (v0.481)", "fc9234beb49804ae869696ce5af8ef30"),
+ FANMADE("SQ2Eye (v0.482)", "3ed84b7b87fa6840f25c15f250a11ffb"),
+ FANMADE("SQ2Eye (v0.483)", "647c31298d3f9cda641231b893e347c0"),
+ FANMADE("SQ2Eye (v0.484)", "f2c86fae7b9046d408c62c8c49a4b882"),
+ FANMADE("SQ2Eye (v0.485)", "af59e36bc28f44545458b68a93e91e67"),
+ FANMADE("SQ2Eye (v0.486)", "3fd86436e93456770dbdd4593eded70a"),
+ FANMADE("Save Santa (v1.0)", "4644f6beb5802081772f14be56ae196c"),
+ FANMADE("Save Santa (v1.3)", "f8afdb6efc5af5e7c0228b44633066af"),
+ FANMADE("Schiller (preview 1)", "ade39dea968c959cfebe1cf935d653e9"),
+ FANMADE("Schiller (preview 2)", "62cd1f8fc758bf6b4aa334e553624cef"),
+ GAME_F("serguei1", "v1.0", "b86725f067e456e10cdbdf5f58e01dec", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ // FIXME: The following two entries have identical MD5 checksums?
+ GAME_F("serguei1", "v1.1 2002 Sep 5", "91975c1fb4b13b0f9a8e9ff74731030d", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ GAME_F("serguei1", "v1.1 2003 Apr 10", "91975c1fb4b13b0f9a8e9ff74731030d", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ GAME_F("serguei2", "v0.1.1 Demo", "906ccbc2ddedb29b63141acc6d10cd28", 0x2917, GF_FANMADE|GF_AGIMOUSE, GID_FANMADE),
+ GAME_F("serguei2", "v1.3.1 Demo (March 22nd 2008)", "ad1308fcb8f48723cd388e012ebf5e20", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ FANMADE("Shifty (v1.0)", "2a07984d27b938364bf6bd243ac75080"),
+ FANMADE_F("Sliding Tile Game (v1.00)", "949bfff5d8a81c3139152eed4d84ca75", GF_AGIMOUSE),
+ FANMADE("Snowboarding Demo (v1.0)", "24bb8f29f1eddb5c0a099705267c86e4"),
+ FANMADE("Solar System Tour", "b5a3d0f392dfd76a6aa63f3d5f578403"),
+ FANMADE("Sorceror's Appraisal", "fe62615557b3cb7b08dd60c9d35efef1"),
+ GAME("sq0", "v1.03", "d2fd6f7404e86182458494e64375e590", 0x2917, GID_FANMADE),
+ GAME("sq0", "v1.04", "2ad9d1a4624a98571ee77dcc83f231b6", 0x2917, GID_FANMADE),
+ GAME_PS("sq0", "", "e1a8e4efcce86e1efcaa14633b9eb986", 762, 0x2440, GID_FANMADE, Common::kPlatformCoCo3),
+ GAME("sqx", "v10.0 Feb 05", "c992ae2f8ab18360404efdf16fa9edd1", 0x2917, GID_FANMADE),
+ GAME("sqx", "v10.0 Jul 18", "812edec45cefad559d190ffde2f9c910", 0x2917, GID_FANMADE),
+ GAME_PS("sqx", "", "f0a59044475a5fa37c055d8c3eb4d1a7", 768, 0x2440, GID_FANMADE, Common::kPlatformCoCo3),
+ FANMADE_F("Space Quest 3.5", "c077bc28d7b36213dd99dc9ecb0147fc", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_F("Space Trek (v1.0)", "807a1aeadb2ace6968831d36ab5ea37a", GF_CLIPCOORDS),
+ FANMADE("Special Delivery", "88764dfe61126b8e73612c851b510a33"),
+ FANMADE("Speeder Bike Challenge (v1.0)", "2deb25bab379285ca955df398d96c1e7"),
+ FANMADE("Star Commander 1 - The Escape (v1.0)", "a7806f01e6fa14ebc029faa58f263750"),
+ FANMADE("Star Pilot: Bigger Fish", "8cb26f8e1c045b75c6576c839d4a0172"),
+ FANMADE_F("Street Quest (Demo)", "cf2aa94a7eb78dce6892c37f03e310d6", GF_AGIPAL),
+ FANMADE("Tales of the Tiki", "8103c9c87e3964690a14a3d0d83f7ddc"),
+ FANMADE("Tex McPhilip 1 - Quest For The Papacy", "3c74b9a24b51aa8020ac82bee3132266"),
+ FANMADE("Tex McPhilip 2 - Road To Divinity (v1.5)", "7387e8df854440bc26620ca0ea43af9a"),
+ FANMADE("Tex McPhilip 3 - A Destiny of Sin (Demo v0.25)", "992d12031a486ad84e592ff5d7c9d782"),
+ FANMADE("The 13th Disciple (v1.00)", "887719ad59afce9a41ec057dbb73ad73"),
+ FANMADE("The Adventures of a Crazed Hermit", "6e3086cbb794d3299a9c5a9792295511"),
+ FANMADE("The Grateful Dead", "c2146631afacf8cb455ce24f3d2d46e7"),
+ FANMADE("The Legend of Shay-Larah 1 - The Lost Prince", "04e720c8e30c9cf12db22ea14a24a3dd"),
+ FANMADE("The Legend of Zelda: The Fungus of Time (Demo v1.00)", "dcaf8166ceb62a3d9b9aea7f3b197c09"),
+ FANMADE("The Legendary Harry Soupsmith (Demo 1998 Apr 2)", "64c46b0d6fc135c9835afa80980d2831"),
+ FANMADE("The Legendary Harry Soupsmith (Demo 1998 Aug 19)", "8d06d82970f2c591d880a95476efbcf0"),
+ FANMADE("The Long Haired Dude: Encounter of the 18-th Kind", "86ea17b9fc2f3e537a7e40863d352c29"),
+ FANMADE("The Lost Planet (v0.9)", "590dffcbd932a9fbe554be13b769cac0"),
+ FANMADE("The Lost Planet (v1.0)", "58564df8b6394612dd4b6f5c0fd68d44"),
+ FANMADE("The New Adventure of Roger Wilco (v1.00)", "e5f0a7cb8d49f66b89114951888ca688"),
+ FANMADE("The Ruby Cast (v0.02)", "ed138e461bb1516e097007e017ab62df"),
+ FANMADE("The Shadow Plan", "c02cd10267e721f4e836b1431f504a0a"),
+ FANMADE("Time Quest (Demo v0.1)", "12e1a6f03ea4b8c5531acd0400b4ed8d"),
+ FANMADE("Time Quest (Demo v0.2)", "7b710608abc99e0861ac59b967bf3f6d"),
+ FANMADE_SVP("Time Quest", "90314f473d8317be5cd1f0306f139aea", 300, 0x2440, Common::kPlatformCoCo3),
+ FANMADE("Tonight The Shrieking Corpses Bleed (Demo v0.11)", "bcc57a7c8d563fa0c333107ae1c0a6e6"),
+ FANMADE("Tonight The Shrieking Corpses Bleed (v1.01)", "36b38f621b38e8d104aa0807302dc8c9"),
+ FANMADE("Turks' Quest - Heir to the Planet", "3d19254b737c8b218e5bc4580542b79a"),
+ FANMADE("URI Quest (v0.173 Feb 27)", "3986eefcf546dafc45f920ae91a697c3"),
+ FANMADE("URI Quest (v0.173 Jan 29)", "494150940d34130605a4f2e67ee40b12"),
+ {
+ // V - The Graphical Adventure
+ {
+ "agi-fanmade",
+ "V - The Graphical Adventure (Demo 2)",
+ AD_ENTRY1s("vdir", "c71f5c1e008d352ae9040b77fcf79327", 3080),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_USEEXTRAASTITLE,
+ GUIO_NONE
+ },
+ GID_FANMADE,
+ GType_V3,
+ GF_FANMADE,
+ 0x3149,
+ },
+ FANMADE_SVP("V - The Graphical Adventure", "1646eaade74f137a9041eb427a389969", 768, 0x2440, Common::kPlatformCoCo3),
+
+ FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Jan 1)", "ae95f0c77d9a97b61420fd192348b937"),
+ FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Mar 29)", "11d0417b7b886f963d0b36789dac4c8f"),
+ FANMADE("Wizaro (v0.1)", "abeec1eda6eaf8dbc52443ea97ff140c"),
+
+ { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
+};
+
+/**
+ * The fallback game descriptor used by the AGI engine's fallbackDetector.
+ * Contents of this struct are to be overwritten by the fallbackDetector.
+ */
+static AGIGameDescription g_fallbackDesc = {
+ {
+ "",
+ "",
+ AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
+ Common::UNK_LANG,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_FANMADE,
+ GType_V2,
+ GF_FANMADE,
+ 0x2917,
+};
+
+} // End of namespace Agi
diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp
index 2bea49a807..62bcd5d8d8 100644
--- a/engines/agi/keyboard.cpp
+++ b/engines/agi/keyboard.cpp
@@ -104,10 +104,10 @@ int AgiEngine::handleController(int key) {
VtEntry *v = &_game.viewTable[0];
int i;
- // AGI 3.149 games and The Black Cauldron need KEY_ESCAPE to use menus
+ // AGI 3.149 games, The Black Cauldron and King's Quest 4 need KEY_ESCAPE to use menus
// Games with the GF_ESCPAUSE flag need KEY_ESCAPE to pause the game
if (key == 0 ||
- (key == KEY_ESCAPE && getVersion() != 0x3149 && getGameID() != GID_BC && !(getFeatures() & GF_ESCPAUSE)) )
+ (key == KEY_ESCAPE && getVersion() != 0x3149 && getGameID() != GID_BC && getGameID() != GID_KQ4 && !(getFeatures() & GF_ESCPAUSE)) )
return false;
if ((getGameID() == GID_MH1 || getGameID() == GID_MH2) && (key == KEY_ENTER) &&
@@ -121,7 +121,7 @@ int AgiEngine::handleController(int key) {
if (_game.controllers[i].keycode == key) {
debugC(3, kDebugLevelInput, "event %d: key press", _game.controllers[i].controller);
_game.controllerOccured[_game.controllers[i].controller] = true;
- report("event AC:%i occured\n", _game.controllers[i].controller);
+ report("event AC:%i occurred\n", _game.controllers[i].controller);
return true;
}
}
@@ -191,9 +191,8 @@ int AgiEngine::handleController(int key) {
}
}
- v->flags &= ~ADJ_EGO_XY;
-
if (d || key == KEY_STATIONARY) {
+ v->flags &= ~ADJ_EGO_XY;
v->direction = v->direction == d ? 0 : d;
return true;
}
@@ -320,7 +319,7 @@ void AgiEngine::handleKeys(int key) {
// Clear to start a new line
_game.hasPrompt = 0;
_game.inputBuffer[_game.cursorPos = 0] = 0;
- debugC(3, kDebugLevelInput, "clear lines");
+ debugC(3, kDebugLevelInput | kDebugLevelText, "clear lines");
clearLines(l, l + 1, bg);
flushLines(l, l + 1);
#ifdef __DS__
diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp
index 3d1c4fa2cf..de6f8d0653 100644
--- a/engines/agi/loader_v2.cpp
+++ b/engines/agi/loader_v2.cpp
@@ -232,7 +232,7 @@ int AgiLoader_v2::loadResource(int t, int n) {
if (data != NULL) {
// Freeing of the raw resource from memory is delegated to the createFromRawResource-function
- _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound);
+ _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound, _vm->_soundemu);
_vm->_game.dirSound[n].flags |= RES_LOADED;
} else {
ec = errBadResource;
diff --git a/engines/agi/loader_v3.cpp b/engines/agi/loader_v3.cpp
index cd97c44521..f145140768 100644
--- a/engines/agi/loader_v3.cpp
+++ b/engines/agi/loader_v3.cpp
@@ -227,19 +227,12 @@ uint8 *AgiLoader_v3::loadVolRes(AgiDir *agid) {
compBuffer = (uint8 *)calloc(1, agid->clen + 32);
fp.read(compBuffer, agid->clen);
- if (x[2] & 0x80 || agid->len == agid->clen) {
+ if (x[2] & 0x80) { // compressed pic
+ data = _vm->_picture->convertV3Pic(compBuffer, agid->clen);
+ // compBuffer has been freed inside convertV3Pic()
+ } else if (agid->len == agid->clen) {
// do not decompress
data = compBuffer;
-
-#if 0
- // CM: added to avoid problems in
- // convert_v2_v3_pic() when clen > len
- // e.g. Sierra demo 4, first picture
- // (Tue Mar 16 13:13:43 EST 1999)
- agid->len = agid->clen;
-
- // Now removed to fix Gold Rush! in demo4
-#endif
} else {
// it is compressed
data = (uint8 *)calloc(1, agid->len + 32);
@@ -309,7 +302,6 @@ int AgiLoader_v3::loadResource(int t, int n) {
unloadResource(rPICTURE, n);
data = loadVolRes(&_vm->_game.dirPic[n]);
if (data != NULL) {
- data = _vm->_picture->convertV3Pic(data, _vm->_game.dirPic[n].len);
_vm->_game.pictures[n].rdata = data;
_vm->_game.dirPic[n].flags |= RES_LOADED;
} else {
@@ -324,7 +316,7 @@ int AgiLoader_v3::loadResource(int t, int n) {
data = loadVolRes(&_vm->_game.dirSound[n]);
if (data != NULL) {
// Freeing of the raw resource from memory is delegated to the createFromRawResource-function
- _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound);
+ _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound, _vm->_soundemu);
_vm->_game.dirSound[n].flags |= RES_LOADED;
} else {
ec = errBadResource;
diff --git a/engines/agi/module.mk b/engines/agi/module.mk
index f031834c9d..2339d1019f 100644
--- a/engines/agi/module.mk
+++ b/engines/agi/module.mk
@@ -30,6 +30,11 @@ MODULE_OBJS := \
predictive.o \
saveload.o \
sound.o \
+ sound_2gs.o \
+ sound_coco3.o \
+ sound_midi.o \
+ sound_pcjr.o \
+ sound_sarien.o \
sprite.o \
text.o \
view.o \
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index d7e3ba416c..a60080186c 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -43,221 +43,201 @@ namespace Agi {
#define p5 (p[5])
#define p6 (p[6])
-#define game g_agi->_game
-#define g_sprites g_agi->_sprites
-#define g_sound g_agi->_sound
-#define g_gfx g_agi->_gfx
-#define g_picture g_agi->_picture
+#define ip _curLogic->cIP
+#define vt _game.viewTable[p0]
+#define vt_v _game.viewTable[_game.vars[p0]]
-#define ip curLogic->cIP
-#define vt game.viewTable[p0]
-#define vt_v game.viewTable[game.vars[p0]]
+#define _v _game.vars
-static struct AgiLogic *curLogic;
-
-int timerHack; // Workaround for timer loop in MH1
-
-#define _v game.vars
-#define cmd(x) static void cmd_##x (AgiEngine *g_agi, uint8 *p)
-
-cmd(increment) {
+void AgiEngine::cmd_increment(uint8 *p) {
if (_v[p0] != 0xff)
++_v[p0];
}
-cmd(decrement) {
+void AgiEngine::cmd_decrement(uint8 *p) {
if (_v[p0] != 0)
--_v[p0];
}
-cmd(assignn) {
+void AgiEngine::cmd_assignn(uint8 *p) {
_v[p0] = p1;
- // WORKAROUND for a bug in fan game "Get outta SQ"
+ // WORKAROUND for a bug in fan _game "Get outta SQ"
// Total number of points is stored in variable 7, which
- // is then incorrectly assigned to 0. Thus, when the game
+ // is then incorrectly assigned to 0. Thus, when the _game
// is restarted, "Points 0 of 0" is shown. We set the
// variable to the correct value here
// Fixes bug #1942476 - "AGI: Fan(Get Outta SQ) - Score
// is lost on restart"
- if (g_agi->getGameID() == GID_GETOUTTASQ && p0 == 7)
+ if (getGameID() == GID_GETOUTTASQ && p0 == 7)
_v[p0] = 8;
}
-cmd(addn) {
+void AgiEngine::cmd_addn(uint8 *p) {
_v[p0] += p1;
}
-cmd(subn) {
+void AgiEngine::cmd_subn(uint8 *p) {
_v[p0] -= p1;
}
-cmd(assignv) {
+void AgiEngine::cmd_assignv(uint8 *p) {
_v[p0] = _v[p1];
}
-cmd(addv) {
+void AgiEngine::cmd_addv(uint8 *p) {
_v[p0] += _v[p1];
}
-cmd(subv) {
+void AgiEngine::cmd_subv(uint8 *p) {
_v[p0] -= _v[p1];
}
-cmd(mul_n) {
+void AgiEngine::cmd_mul_n(uint8 *p) {
_v[p0] *= p1;
}
-cmd(mul_v) {
+void AgiEngine::cmd_mul_v(uint8 *p) {
_v[p0] *= _v[p1];
}
-cmd(div_n) {
+void AgiEngine::cmd_div_n(uint8 *p) {
_v[p0] /= p1;
}
-cmd(div_v) {
+void AgiEngine::cmd_div_v(uint8 *p) {
_v[p0] /= _v[p1];
}
-cmd(random) {
- _v[p2] = g_agi->_rnd->getRandomNumber(p1 - p0) + p0;
+void AgiEngine::cmd_random(uint8 *p) {
+ _v[p2] = _rnd->getRandomNumber(p1 - p0) + p0;
}
-cmd(lindirectn) {
+void AgiEngine::cmd_lindirectn(uint8 *p) {
_v[_v[p0]] = p1;
}
-cmd(lindirectv) {
+void AgiEngine::cmd_lindirectv(uint8 *p) {
_v[_v[p0]] = _v[p1];
}
-cmd(rindirect) {
+void AgiEngine::cmd_rindirect(uint8 *p) {
_v[p0] = _v[_v[p1]];
}
-cmd(set) {
- g_agi->setflag(*p, true);
+void AgiEngine::cmd_set(uint8 *p) {
+ setflag(*p, true);
}
-cmd(reset) {
- g_agi->setflag(*p, false);
+void AgiEngine::cmd_reset(uint8 *p) {
+ setflag(*p, false);
}
-cmd(toggle) {
- g_agi->setflag(*p, !g_agi->getflag(*p));
+void AgiEngine::cmd_toggle(uint8 *p) {
+ setflag(*p, !getflag(*p));
}
-cmd(set_v) {
- g_agi->setflag(_v[p0], true);
+void AgiEngine::cmd_set_v(uint8 *p) {
+ setflag(_v[p0], true);
}
-cmd(reset_v) {
- g_agi->setflag(_v[p0], false);
+void AgiEngine::cmd_reset_v(uint8 *p) {
+ setflag(_v[p0], false);
}
-cmd(toggle_v) {
- g_agi->setflag(_v[p0], !g_agi->getflag(_v[p0]));
+void AgiEngine::cmd_toggle_v(uint8 *p) {
+ setflag(_v[p0], !getflag(_v[p0]));
}
-cmd(new_room) {
- g_agi->newRoom(p0);
+void AgiEngine::cmd_new_room(uint8 *p) {
+ newRoom(p0);
// WORKAROUND: Works around intro skipping bug (#1737343) in Gold Rush.
// Intro was skipped because the enter-keypress finalizing the entering
// of the copy protection string (Copy protection is in logic.128) was
// left over to the intro scene (Starts with room 73 i.e. logic.073).
// The intro scene checks for any keys pressed and if it finds any it
- // jumps to the game's start (Room 1 i.e. logic.001). We clear the
+ // jumps to the _game's start (Room 1 i.e. logic.001). We clear the
// keyboard buffer when the intro sequence's first room (Room 73) is
// loaded so that no keys from the copy protection scene can be left
- // over to cause the intro to skip to the game's start.
- if (g_agi->getGameID() == GID_GOLDRUSH && p0 == 73)
- game.keypress = 0;
+ // over to cause the intro to skip to the _game's start.
+ if (getGameID() == GID_GOLDRUSH && p0 == 73)
+ _game.keypress = 0;
}
-cmd(new_room_f) {
- g_agi->newRoom(_v[p0]);
+void AgiEngine::cmd_new_room_f(uint8 *p) {
+ newRoom(_v[p0]);
}
-cmd(load_view) {
- g_agi->agiLoadResource(rVIEW, p0);
+void AgiEngine::cmd_load_view(uint8 *p) {
+ agiLoadResource(rVIEW, p0);
}
-cmd(load_logic) {
- g_agi->agiLoadResource(rLOGIC, p0);
+void AgiEngine::cmd_load_logic(uint8 *p) {
+ agiLoadResource(rLOGIC, p0);
}
-cmd(load_sound) {
- g_agi->agiLoadResource(rSOUND, p0);
+void AgiEngine::cmd_load_sound(uint8 *p) {
+ agiLoadResource(rSOUND, p0);
}
-cmd(load_view_f) {
- g_agi->agiLoadResource(rVIEW, _v[p0]);
+void AgiEngine::cmd_load_view_f(uint8 *p) {
+ agiLoadResource(rVIEW, _v[p0]);
}
-cmd(load_logic_f) {
- g_agi->agiLoadResource(rLOGIC, _v[p0]);
+void AgiEngine::cmd_load_logic_f(uint8 *p) {
+ agiLoadResource(rLOGIC, _v[p0]);
}
-cmd(discard_view) {
- g_agi->agiUnloadResource(rVIEW, p0);
+void AgiEngine::cmd_discard_view(uint8 *p) {
+ agiUnloadResource(rVIEW, p0);
}
-cmd(object_on_anything) {
+void AgiEngine::cmd_object_on_anything(uint8 *p) {
vt.flags &= ~(ON_WATER | ON_LAND);
}
-cmd(object_on_land) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_object_on_land(uint8 *p) {
vt.flags |= ON_LAND;
}
-cmd(object_on_water) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_object_on_water(uint8 *p) {
vt.flags |= ON_WATER;
}
-cmd(observe_horizon) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_observe_horizon(uint8 *p) {
vt.flags &= ~IGNORE_HORIZON;
}
-cmd(ignore_horizon) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_ignore_horizon(uint8 *p) {
vt.flags |= IGNORE_HORIZON;
}
-cmd(observe_objs) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_observe_objs(uint8 *p) {
vt.flags &= ~IGNORE_OBJECTS;
}
-cmd(ignore_objs) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_ignore_objs(uint8 *p) {
vt.flags |= IGNORE_OBJECTS;
}
-cmd(observe_blocks) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_observe_blocks(uint8 *p) {
vt.flags &= ~IGNORE_BLOCKS;
}
-cmd(ignore_blocks) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_ignore_blocks(uint8 *p) {
vt.flags |= IGNORE_BLOCKS;
}
-cmd(set_horizon) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- game.horizon = p0;
+void AgiEngine::cmd_set_horizon(uint8 *p) {
+ _game.horizon = p0;
}
-cmd(get_priority) {
+void AgiEngine::cmd_get_priority(uint8 *p) {
_v[p1] = vt.priority;
}
-cmd(set_priority) {
+void AgiEngine::cmd_set_priority(uint8 *p) {
vt.flags |= FIXED_PRIORITY;
vt.priority = p1;
@@ -268,259 +248,256 @@ cmd(set_priority) {
// It seems that in this scene, ego's priority is set to 8, but the priority of
// the last dwarf with the soup bowls (view 152) is also set to 8, which causes
// the dwarf to be drawn behind ego
- // With this workaround, when the game scripts set the priority of view 152
+ // With this workaround, when the _game scripts set the priority of view 152
// (seventh dwarf with soup bowls), ego's priority is set to 7
- // The game script itself sets priotity 8 for ego before she starts walking,
+ // The _game script itself sets priotity 8 for ego before she starts walking,
// and then releases the fixed priority set on ego after ego is seated
// Therefore, this workaround only affects that specific part of this scene
// Ego is set to object 19 by script 54
- if (g_agi->getGameID() == GID_KQ4 && vt.currentView == 152) {
- game.viewTable[19].flags |= FIXED_PRIORITY;
- game.viewTable[19].priority = 7;
+ if (getGameID() == GID_KQ4 && vt.currentView == 152) {
+ _game.viewTable[19].flags |= FIXED_PRIORITY;
+ _game.viewTable[19].priority = 7;
}
}
-cmd(set_priority_f) {
+void AgiEngine::cmd_set_priority_f(uint8 *p) {
vt.flags |= FIXED_PRIORITY;
vt.priority = _v[p1];
}
-cmd(release_priority) {
+void AgiEngine::cmd_release_priority(uint8 *p) {
vt.flags &= ~FIXED_PRIORITY;
}
-cmd(set_upper_left) { // do nothing (AGI 2.917)
+void AgiEngine::cmd_set_upper_left(uint8 *p) { // do nothing (AGI 2.917)
}
-cmd(start_update) {
- g_agi->startUpdate(&vt);
+void AgiEngine::cmd_start_update(uint8 *p) {
+ startUpdate(&vt);
}
-cmd(stop_update) {
- g_agi->stopUpdate(&vt);
+void AgiEngine::cmd_stop_update(uint8 *p) {
+ stopUpdate(&vt);
}
-cmd(current_view) {
+void AgiEngine::cmd_current_view(uint8 *p) {
_v[p1] = vt.currentView;
}
-cmd(current_cel) {
+void AgiEngine::cmd_current_cel(uint8 *p) {
_v[p1] = vt.currentCel;
debugC(4, kDebugLevelScripts, "v%d=%d", p1, _v[p1]);
}
-cmd(current_loop) {
+void AgiEngine::cmd_current_loop(uint8 *p) {
_v[p1] = vt.currentLoop;
}
-cmd(last_cel) {
+void AgiEngine::cmd_last_cel(uint8 *p) {
_v[p1] = vt.loopData->numCels - 1;
}
-cmd(set_cel) {
- g_agi->setCel(&vt, p1);
+void AgiEngine::cmd_set_cel(uint8 *p) {
+ setCel(&vt, p1);
vt.flags &= ~DONTUPDATE;
}
-cmd(set_cel_f) {
- g_agi->setCel(&vt, _v[p1]);
+void AgiEngine::cmd_set_cel_f(uint8 *p) {
+ setCel(&vt, _v[p1]);
vt.flags &= ~DONTUPDATE;
}
-cmd(set_view) {
- debugC(4, kDebugLevelScripts, "o%d, %d", p0, p1);
- g_agi->setView(&vt, p1);
+void AgiEngine::cmd_set_view(uint8 *p) {
+ setView(&vt, p1);
}
-cmd(set_view_f) {
- g_agi->setView(&vt, _v[p1]);
+void AgiEngine::cmd_set_view_f(uint8 *p) {
+ setView(&vt, _v[p1]);
}
-cmd(set_loop) {
- g_agi->setLoop(&vt, p1);
+void AgiEngine::cmd_set_loop(uint8 *p) {
+ setLoop(&vt, p1);
}
-cmd(set_loop_f) {
- g_agi->setLoop(&vt, _v[p1]);
+void AgiEngine::cmd_set_loop_f(uint8 *p) {
+ setLoop(&vt, _v[p1]);
}
-cmd(number_of_loops) {
+void AgiEngine::cmd_number_of_loops(uint8 *p) {
_v[p1] = vt.numLoops;
}
-cmd(fix_loop) {
+void AgiEngine::cmd_fix_loop(uint8 *p) {
vt.flags |= FIX_LOOP;
}
-cmd(release_loop) {
+void AgiEngine::cmd_release_loop(uint8 *p) {
vt.flags &= ~FIX_LOOP;
}
-cmd(step_size) {
+void AgiEngine::cmd_step_size(uint8 *p) {
vt.stepSize = _v[p1];
}
-cmd(step_time) {
+void AgiEngine::cmd_step_time(uint8 *p) {
vt.stepTime = vt.stepTimeCount = _v[p1];
}
-cmd(cycle_time) {
+void AgiEngine::cmd_cycle_time(uint8 *p) {
vt.cycleTime = vt.cycleTimeCount = _v[p1];
}
-cmd(stop_cycling) {
+void AgiEngine::cmd_stop_cycling(uint8 *p) {
vt.flags &= ~CYCLING;
}
-cmd(start_cycling) {
+void AgiEngine::cmd_start_cycling(uint8 *p) {
vt.flags |= CYCLING;
}
-cmd(normal_cycle) {
+void AgiEngine::cmd_normal_cycle(uint8 *p) {
vt.cycle = CYCLE_NORMAL;
vt.flags |= CYCLING;
}
-cmd(reverse_cycle) {
+void AgiEngine::cmd_reverse_cycle(uint8 *p) {
vt.cycle = CYCLE_REVERSE;
vt.flags |= CYCLING;
}
-cmd(set_dir) {
+void AgiEngine::cmd_set_dir(uint8 *p) {
vt.direction = _v[p1];
}
-cmd(get_dir) {
+void AgiEngine::cmd_get_dir(uint8 *p) {
_v[p1] = vt.direction;
}
-cmd(get_room_f) {
- _v[p1] = g_agi->objectGetLocation(_v[p0]);
+void AgiEngine::cmd_get_room_f(uint8 *p) {
+ _v[p1] = objectGetLocation(_v[p0]);
}
-cmd(put) {
- g_agi->objectSetLocation(p0, _v[p1]);
+void AgiEngine::cmd_put(uint8 *p) {
+ objectSetLocation(p0, _v[p1]);
}
-cmd(put_f) {
- g_agi->objectSetLocation(_v[p0], _v[p1]);
+void AgiEngine::cmd_put_f(uint8 *p) {
+ objectSetLocation(_v[p0], _v[p1]);
}
-cmd(drop) {
- g_agi->objectSetLocation(p0, 0);
+void AgiEngine::cmd_drop(uint8 *p) {
+ objectSetLocation(p0, 0);
}
-cmd(get) {
- g_agi->objectSetLocation(p0, EGO_OWNED);
+void AgiEngine::cmd_get(uint8 *p) {
+ objectSetLocation(p0, EGO_OWNED);
}
-cmd(get_f) {
- g_agi->objectSetLocation(_v[p0], EGO_OWNED);
+void AgiEngine::cmd_get_f(uint8 *p) {
+ objectSetLocation(_v[p0], EGO_OWNED);
}
-cmd(word_to_string) {
- strcpy(game.strings[p0], game.egoWords[p1].word);
+void AgiEngine::cmd_word_to_string(uint8 *p) {
+ strcpy(_game.strings[p0], _game.egoWords[p1].word);
}
-cmd(open_dialogue) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- game.hasWindow = true;
+void AgiEngine::cmd_open_dialogue(uint8 *p) {
+ _game.hasWindow = true;
}
-cmd(close_dialogue) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- game.hasWindow = false;
+void AgiEngine::cmd_close_dialogue(uint8 *p) {
+ _game.hasWindow = false;
}
-cmd(close_window) {
- g_agi->closeWindow();
+void AgiEngine::cmd_close_window(uint8 *p) {
+ closeWindow();
}
-cmd(status_line_on) {
- game.statusLine = true;
- g_agi->writeStatus();
+void AgiEngine::cmd_status_line_on(uint8 *p) {
+ _game.statusLine = true;
+ writeStatus();
}
-cmd(status_line_off) {
- game.statusLine = false;
- g_agi->writeStatus();
+void AgiEngine::cmd_status_line_off(uint8 *p) {
+ _game.statusLine = false;
+ writeStatus();
}
-cmd(show_obj) {
- g_sprites->showObj(p0);
+void AgiEngine::cmd_show_obj(uint8 *p) {
+ _sprites->showObj(p0);
}
-cmd(show_obj_v) {
- g_sprites->showObj(_v[p0]);
+void AgiEngine::cmd_show_obj_v(uint8 *p) {
+ _sprites->showObj(_v[p0]);
}
-cmd(sound) {
- g_sound->startSound(p0, p1);
+void AgiEngine::cmd_sound(uint8 *p) {
+ _sound->startSound(p0, p1);
}
-cmd(stop_sound) {
- g_sound->stopSound();
+void AgiEngine::cmd_stop_sound(uint8 *p) {
+ _sound->stopSound();
}
-cmd(menu_input) {
- g_agi->newInputMode(INPUT_MENU);
+void AgiEngine::cmd_menu_input(uint8 *p) {
+ newInputMode(INPUT_MENU);
}
-cmd(enable_item) {
- g_agi->_menu->setItem(p0, true);
+void AgiEngine::cmd_enable_item(uint8 *p) {
+ _menu->setItem(p0, true);
}
-cmd(disable_item) {
- g_agi->_menu->setItem(p0, false);
+void AgiEngine::cmd_disable_item(uint8 *p) {
+ _menu->setItem(p0, false);
}
-cmd(submit_menu) {
- g_agi->_menu->submit();
+void AgiEngine::cmd_submit_menu(uint8 *p) {
+ _menu->submit();
}
-cmd(set_scan_start) {
- curLogic->sIP = curLogic->cIP;
+void AgiEngine::cmd_set_scan_start(uint8 *p) {
+ _curLogic->sIP = _curLogic->cIP;
}
-cmd(reset_scan_start) {
- curLogic->sIP = 2;
+void AgiEngine::cmd_reset_scan_start(uint8 *p) {
+ _curLogic->sIP = 2;
}
-cmd(save_game) {
- game.simpleSave ? g_agi->saveGameSimple() : g_agi->saveGameDialog();
+void AgiEngine::cmd_save_game(uint8 *p) {
+ _game.simpleSave ? saveGameSimple() : saveGameDialog();
}
-cmd(load_game) {
+void AgiEngine::cmd_load_game(uint8 *p) {
assert(1);
- game.simpleSave ? g_agi->loadGameSimple() : g_agi->loadGameDialog();
+ _game.simpleSave ? loadGameSimple() : loadGameDialog();
}
-cmd(init_disk) { // do nothing
+void AgiEngine::cmd_init_disk(uint8 *p) { // do nothing
}
-cmd(log) { // do nothing
+void AgiEngine::cmd_log(uint8 *p) { // do nothing
}
-cmd(trace_on) { // do nothing
+void AgiEngine::cmd_trace_on(uint8 *p) { // do nothing
}
-cmd(trace_info) { // do nothing
+void AgiEngine::cmd_trace_info(uint8 *p) { // do nothing
}
-cmd(show_mem) {
- g_agi->messageBox("Enough memory");
+void AgiEngine::cmd_show_mem(uint8 *p) {
+ messageBox("Enough memory");
}
-cmd(init_joy) { // do nothing
+void AgiEngine::cmd_init_joy(uint8 *p) { // do nothing
}
-cmd(script_size) {
+void AgiEngine::cmd_script_size(uint8 *p) {
report("script.size(%d)\n", p0);
}
-cmd(cancel_line) {
- g_agi->_game.inputBuffer[0] = 0;
- g_agi->writePrompt();
+void AgiEngine::cmd_cancel_line(uint8 *p) {
+ _game.inputBuffer[0] = 0;
+ writePrompt();
}
// This implementation is based on observations of Amiga's Gold Rush.
@@ -533,7 +510,7 @@ cmd(cancel_line) {
// 4051 (When ego is stationary),
// 471 (When walking on the first screen's bridge),
// 71 (When walking around, using the mouse or the keyboard).
-cmd(obj_status_f) {
+void AgiEngine::cmd_obj_status_f(uint8 *p) {
const char *cycleDesc; // Object's cycle description line
const char *motionDesc; // Object's motion description line
char msg[256]; // The whole object status message
@@ -594,7 +571,7 @@ cmd(obj_status_f) {
vt_v.stepSize,
cycleDesc,
motionDesc);
- g_agi->messageBox(msg);
+ messageBox(msg);
}
// unknown commands:
@@ -605,49 +582,49 @@ cmd(obj_status_f) {
// unk_174: Change priority table (used in KQ4) -- j5
// unk_177: Disable menus completely -- j5
// unk_181: Deactivate keypressed control (default control of ego)
-cmd(set_simple) {
- if (!(g_agi->getFeatures() & (GF_AGI256 | GF_AGI256_2))) {
- game.simpleSave = true;
+void AgiEngine::cmd_set_simple(uint8 *p) {
+ if (!(getFeatures() & (GF_AGI256 | GF_AGI256_2))) {
+ _game.simpleSave = true;
} else { // AGI256 and AGI256-2 use this unknown170 command to load 256 color pictures.
- // Load the picture. Similar to cmd(load_pic).
- g_sprites->eraseBoth();
- g_agi->agiLoadResource(rPICTURE, _v[p0]);
+ // Load the picture. Similar to void AgiEngine::cmd_load_pic(uint8 *p).
+ _sprites->eraseBoth();
+ agiLoadResource(rPICTURE, _v[p0]);
- // Draw the picture. Similar to cmd(draw_pic).
- g_picture->decodePicture(_v[p0], false, true);
- g_sprites->blitBoth();
- game.pictureShown = 0;
+ // Draw the picture. Similar to void AgiEngine::cmd_draw_pic(uint8 *p).
+ _picture->decodePicture(_v[p0], false, true);
+ _sprites->blitBoth();
+ _game.pictureShown = 0;
- // Show the picture. Similar to cmd(show_pic).
- g_agi->setflag(fOutputMode, false);
- cmd_close_window(g_agi, NULL);
- g_picture->showPic();
- game.pictureShown = 1;
+ // Show the picture. Similar to void AgiEngine::cmd_show_pic(uint8 *p).
+ setflag(fOutputMode, false);
+ closeWindow();
+ _picture->showPic();
+ _game.pictureShown = 1;
// Simulate slowww computer. Many effects rely on this
- g_agi->pause(kPausePicture);
+ pause(kPausePicture);
}
}
-cmd(pop_script) {
- if (g_agi->getVersion() >= 0x2915) {
+void AgiEngine::cmd_pop_script(uint8 *p) {
+ if (getVersion() >= 0x2915) {
report("pop.script\n");
}
}
-cmd(hold_key) {
- if (g_agi->getVersion() >= 0x3098) {
- g_agi->_egoHoldKey = true;
+void AgiEngine::cmd_hold_key(uint8 *p) {
+ if (getVersion() >= 0x3098) {
+ _egoHoldKey = true;
}
}
-cmd(discard_sound) {
- if (g_agi->getVersion() >= 0x2936) {
+void AgiEngine::cmd_discard_sound(uint8 *p) {
+ if (getVersion() >= 0x2936) {
report("discard.sound\n");
}
}
-cmd(hide_mouse) {
+void AgiEngine::cmd_hide_mouse(uint8 *p) {
// WORKAROUND: Turns off current movement that's being caused with the mouse.
// This fixes problems with too many popup boxes appearing in the Amiga
// Gold Rush's copy protection failure scene (i.e. the hanging scene, logic.192).
@@ -655,34 +632,34 @@ cmd(hide_mouse) {
// to walk somewhere else than to the right using the mouse.
// FIXME: Write a proper implementation using disassembly and
// apply it to other games as well if applicable.
- game.viewTable[0].flags &= ~ADJ_EGO_XY;
+ _game.viewTable[0].flags &= ~ADJ_EGO_XY;
g_system->showMouse(false);
}
-cmd(allow_menu) {
- if (g_agi->getVersion() >= 0x3098) {
- g_agi->setflag(fMenusWork, ((p0 != 0) ? true : false));
+void AgiEngine::cmd_allow_menu(uint8 *p) {
+ if (getVersion() >= 0x3098) {
+ setflag(fMenusWork, ((p0 != 0) ? true : false));
}
}
-cmd(show_mouse) {
+void AgiEngine::cmd_show_mouse(uint8 *p) {
g_system->showMouse(true);
}
-cmd(fence_mouse) {
- g_agi->_game.mouseFence.moveTo(p0, p1);
- g_agi->_game.mouseFence.setWidth(p2 - p0);
- g_agi->_game.mouseFence.setHeight(p3 - p1);
+void AgiEngine::cmd_fence_mouse(uint8 *p) {
+ _game.mouseFence.moveTo(p0, p1);
+ _game.mouseFence.setWidth(p2 - p0);
+ _game.mouseFence.setHeight(p3 - p1);
}
-cmd(release_key) {
- if (g_agi->getVersion() >= 0x3098) {
- g_agi->_egoHoldKey = false;
+void AgiEngine::cmd_release_key(uint8 *p) {
+ if (getVersion() >= 0x3098) {
+ _egoHoldKey = false;
}
}
-cmd(adj_ego_move_to_x_y) {
+void AgiEngine::cmd_adj_ego_move_to_x_y(uint8 *p) {
int8 x, y;
switch (logicNamesCmd[182].numArgs) {
@@ -704,56 +681,57 @@ cmd(adj_ego_move_to_x_y) {
// onto the ladder so this is more like it (Although that may be caused
// by something else because this command doesn't do any flag manipulations
// in the Amiga version - checked it with disassembly).
- if (x != game.adjMouseX || y != game.adjMouseY)
- game.viewTable[EGO_VIEW_TABLE].flags &= ~ADJ_EGO_XY;
+ if (x != _game.adjMouseX || y != _game.adjMouseY)
+ _game.viewTable[EGO_VIEW_TABLE].flags &= ~ADJ_EGO_XY;
- game.adjMouseX = x;
- game.adjMouseY = y;
+ _game.adjMouseX = x;
+ _game.adjMouseY = y;
debugC(4, kDebugLevelScripts, "adj.ego.move.to.x.y(%d, %d)", x, y);
break;
// TODO: Check where (if anywhere) the 0 arguments version is used
case 0:
default:
- game.viewTable[0].flags |= ADJ_EGO_XY;
+ _game.viewTable[0].flags |= ADJ_EGO_XY;
break;
}
}
-cmd(parse) {
+void AgiEngine::cmd_parse(uint8 *p) {
_v[vWordNotFound] = 0;
- g_agi->setflag(fEnteredCli, false);
- g_agi->setflag(fSaidAcceptedInput, false);
+ setflag(fEnteredCli, false);
+ setflag(fSaidAcceptedInput, false);
- g_agi->dictionaryWords(g_agi->agiSprintf(game.strings[p0]));
+ dictionaryWords(agiSprintf(_game.strings[p0]));
}
-cmd(call) {
+void AgiEngine::cmd_call(uint8 *p) {
int oldCIP;
int oldLognum;
// CM: we don't save sIP because set.scan.start can be
// used in a called script (fixes xmas demo)
- oldCIP = curLogic->cIP;
- oldLognum = game.lognum;
+ oldCIP = _curLogic->cIP;
+ oldLognum = _game.lognum;
- g_agi->runLogic(p0);
+ runLogic(p0);
- game.lognum = oldLognum;
- curLogic = &game.logics[game.lognum];
- curLogic->cIP = oldCIP;
+ _game.lognum = oldLognum;
+ _curLogic = &_game.logics[_game.lognum];
+ _curLogic->cIP = oldCIP;
}
-cmd(call_f) {
- cmd_call(g_agi, &_v[p0]);
+void AgiEngine::cmd_call_f(uint8 *p) {
+ cmd_call(&_v[p0]);
}
-cmd(draw_pic) {
+void AgiEngine::cmd_draw_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "=== draw pic %d ===", _v[p0]);
- g_sprites->eraseBoth();
- g_picture->decodePicture(_v[p0], true);
- g_sprites->blitBoth();
- game.pictureShown = 0;
+ _sprites->eraseBoth();
+ _picture->decodePicture(_v[p0], true);
+ _sprites->blitBoth();
+ _sprites->commitBoth();
+ _game.pictureShown = 0;
debugC(6, kDebugLevelScripts, "--- end of draw pic %d ---", _v[p0]);
// WORKAROUND for a script bug which exists in SQ1, logic scripts
@@ -768,63 +746,64 @@ cmd(draw_pic) {
// above the ground), flag 103 is reset, thereby fixing this issue. Note
// that this is a script bug and occurs in the original interpreter as well.
// Fixes bug #1658514: AGI: SQ1 (2.2 DOS ENG) bizzare exploding roger
- if (g_agi->getGameID() == GID_SQ1 && _v[p0] == 20)
- g_agi->setflag(103, false);
+ if (getGameID() == GID_SQ1 && _v[p0] == 20)
+ setflag(103, false);
// Simulate slowww computer. Many effects rely on this
- g_agi->pause(kPausePicture);
+ pause(kPausePicture);
}
-cmd(show_pic) {
+void AgiEngine::cmd_show_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "=== show pic ===");
- g_agi->setflag(fOutputMode, false);
- cmd_close_window(g_agi, NULL);
- g_picture->showPic();
- game.pictureShown = 1;
+ setflag(fOutputMode, false);
+ closeWindow();
+ _picture->showPic();
+ _game.pictureShown = 1;
debugC(6, kDebugLevelScripts, "--- end of show pic ---");
}
-cmd(load_pic) {
- g_sprites->eraseBoth();
- g_agi->agiLoadResource(rPICTURE, _v[p0]);
- g_sprites->blitBoth();
+void AgiEngine::cmd_load_pic(uint8 *p) {
+ _sprites->eraseBoth();
+ agiLoadResource(rPICTURE, _v[p0]);
+ _sprites->blitBoth();
+ _sprites->commitBoth();
}
-cmd(discard_pic) {
+void AgiEngine::cmd_discard_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "--- discard pic ---");
// do nothing
}
-cmd(overlay_pic) {
+void AgiEngine::cmd_overlay_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "--- overlay pic ---");
- g_sprites->eraseBoth();
- g_picture->decodePicture(_v[p0], false);
- g_sprites->blitBoth();
- game.pictureShown = 0;
- g_sprites->commitBoth();
+ _sprites->eraseBoth();
+ _picture->decodePicture(_v[p0], false);
+ _sprites->blitBoth();
+ _game.pictureShown = 0;
+ _sprites->commitBoth();
// Simulate slowww computer. Many effects rely on this
- g_agi->pause(kPausePicture);
+ pause(kPausePicture);
}
-cmd(show_pri_screen) {
- g_agi->_debug.priority = 1;
- g_sprites->eraseBoth();
- g_picture->showPic();
- g_sprites->blitBoth();
+void AgiEngine::cmd_show_pri_screen(uint8 *p) {
+ _debug.priority = 1;
+ _sprites->eraseBoth();
+ _picture->showPic();
+ _sprites->blitBoth();
- g_agi->waitKey();
+ waitKey();
- g_agi->_debug.priority = 0;
- g_sprites->eraseBoth();
- g_picture->showPic();
- g_sprites->blitBoth();
+ _debug.priority = 0;
+ _sprites->eraseBoth();
+ _picture->showPic();
+ _sprites->blitBoth();
}
-cmd(animate_obj) {
+void AgiEngine::cmd_animate_obj(uint8 *p) {
if (vt.flags & ANIMATED)
return;
@@ -835,14 +814,14 @@ cmd(animate_obj) {
vt.direction = 0;
}
-cmd(unanimate_all) {
+void AgiEngine::cmd_unanimate_all(uint8 *p) {
int i;
for (i = 0; i < MAX_VIEWTABLE; i++)
- game.viewTable[i].flags &= ~(ANIMATED | DRAWN);
+ _game.viewTable[i].flags &= ~(ANIMATED | DRAWN);
}
-cmd(draw) {
+void AgiEngine::cmd_draw(uint8 *p) {
if (vt.flags & DRAWN)
return;
@@ -852,19 +831,19 @@ cmd(draw) {
debugC(4, kDebugLevelScripts, "draw entry %d", vt.entry);
vt.flags |= UPDATE;
- if (g_agi->getVersion() >= 0x3000) {
- g_agi->setLoop(&vt, vt.currentLoop);
- g_agi->setCel(&vt, vt.currentCel);
+ if (getVersion() >= 0x3000) {
+ setLoop(&vt, vt.currentLoop);
+ setCel(&vt, vt.currentCel);
}
- g_agi->fixPosition(p0);
+ fixPosition(p0);
vt.xPos2 = vt.xPos;
vt.yPos2 = vt.yPos;
vt.celData2 = vt.celData;
- g_sprites->eraseUpdSprites();
+ _sprites->eraseUpdSprites();
vt.flags |= DRAWN;
- // WORKAROUND: This fixes a bug with AGI Fanmade game Space Trek.
+ // WORKAROUND: This fixes a bug with AGI Fanmade _game Space Trek.
// The original workaround checked if AGI version was <= 2.440, which could
// cause regressions with some AGI games. The original workaround no longer
// works for Space Trek in ScummVM, as all fanmade games are set to use
@@ -875,36 +854,43 @@ cmd(draw) {
// TODO: Investigate this further and check if any other fanmade AGI
// games are affected. If yes, then it'd be best to set this for Space
// Trek only
- if (g_agi->getFeatures() & GF_FANMADE) // See Sarien bug #546562
+ if (getFeatures() & GF_FANMADE) // See Sarien bug #546562
vt.flags |= ANIMATED;
- g_sprites->blitUpdSprites();
+ _sprites->blitUpdSprites();
vt.flags &= ~DONTUPDATE;
- g_sprites->commitBlock(vt.xPos, vt.yPos - vt.ySize + 1, vt.xPos + vt.xSize - 1, vt.yPos);
+ _sprites->commitBlock(vt.xPos, vt.yPos - vt.ySize + 1, vt.xPos + vt.xSize - 1, vt.yPos, true);
debugC(4, kDebugLevelScripts, "vt entry #%d flags = %02x", p0, vt.flags);
}
-cmd(erase) {
+void AgiEngine::cmd_erase(uint8 *p) {
if (~vt.flags & DRAWN)
return;
- g_sprites->eraseUpdSprites();
+ _sprites->eraseUpdSprites();
if (vt.flags & UPDATE) {
vt.flags &= ~DRAWN;
} else {
- g_sprites->eraseNonupdSprites();
+ _sprites->eraseNonupdSprites();
vt.flags &= ~DRAWN;
- g_sprites->blitNonupdSprites();
+ _sprites->blitNonupdSprites();
}
- g_sprites->blitUpdSprites();
+ _sprites->blitUpdSprites();
+
+ int x1, y1, x2, y2;
+
+ x1 = MIN((int)MIN(vt.xPos, vt.xPos2), MIN(vt.xPos + vt.celData->width, vt.xPos2 + vt.celData2->width));
+ x2 = MAX((int)MAX(vt.xPos, vt.xPos2), MAX(vt.xPos + vt.celData->width, vt.xPos2 + vt.celData2->width));
+ y1 = MIN((int)MIN(vt.yPos, vt.yPos2), MIN(vt.yPos - vt.celData->height, vt.yPos2 - vt.celData2->height));
+ y2 = MAX((int)MAX(vt.yPos, vt.yPos2), MAX(vt.yPos - vt.celData->height, vt.yPos2 - vt.celData2->height));
- g_sprites->commitBlock(vt.xPos, vt.yPos - vt.ySize + 1, vt.xPos + vt.xSize - 1, vt.yPos);
+ _sprites->commitBlock(x1, y1, x2, y2, true);
}
-cmd(position) {
+void AgiEngine::cmd_position(uint8 *p) {
vt.xPos = vt.xPos2 = p1;
vt.yPos = vt.yPos2 = p2;
@@ -921,27 +907,27 @@ cmd(position) {
// I haven't checked but if Space Trek solely abuses the position-command we wouldn't
// strictly need the identical workaround in the position.v-command but it does make
// for a nice symmetry.
- if (g_agi->getFeatures() & GF_CLIPCOORDS)
- g_agi->clipViewCoordinates(&vt);
+ if (getFeatures() & GF_CLIPCOORDS)
+ clipViewCoordinates(&vt);
}
-cmd(position_f) {
+void AgiEngine::cmd_position_f(uint8 *p) {
vt.xPos = vt.xPos2 = _v[p1];
vt.yPos = vt.yPos2 = _v[p2];
// WORKAROUND: Part of the fix for bug #1659209 "AGI: Space Trek sprite duplication"
// with an accompanying identical workaround in position-command (i.e. command 0x25).
// See that workaround's comment for more in-depth information.
- if (g_agi->getFeatures() & GF_CLIPCOORDS)
- g_agi->clipViewCoordinates(&vt);
+ if (getFeatures() & GF_CLIPCOORDS)
+ clipViewCoordinates(&vt);
}
-cmd(get_posn) {
- game.vars[p1] = (unsigned char)vt.xPos;
- game.vars[p2] = (unsigned char)vt.yPos;
+void AgiEngine::cmd_get_posn(uint8 *p) {
+ _game.vars[p1] = (unsigned char)vt.xPos;
+ _game.vars[p2] = (unsigned char)vt.yPos;
}
-cmd(reposition) {
+void AgiEngine::cmd_reposition(uint8 *p) {
int dx = (int8) _v[p1], dy = (int8) _v[p2];
debugC(4, kDebugLevelScripts, "dx=%d, dy=%d", dx, dy);
@@ -957,106 +943,106 @@ cmd(reposition) {
else
vt.yPos += dy;
- g_agi->fixPosition(p0);
+ fixPosition(p0);
}
-cmd(reposition_to) {
+void AgiEngine::cmd_reposition_to(uint8 *p) {
vt.xPos = p1;
vt.yPos = p2;
vt.flags |= UPDATE_POS;
- g_agi->fixPosition(p0);
+ fixPosition(p0);
}
-cmd(reposition_to_f) {
+void AgiEngine::cmd_reposition_to_f(uint8 *p) {
vt.xPos = _v[p1];
vt.yPos = _v[p2];
vt.flags |= UPDATE_POS;
- g_agi->fixPosition(p0);
+ fixPosition(p0);
}
-cmd(add_to_pic) {
- g_sprites->addToPic(p0, p1, p2, p3, p4, p5, p6);
+void AgiEngine::cmd_add_to_pic(uint8 *p) {
+ _sprites->addToPic(p0, p1, p2, p3, p4, p5, p6);
}
-cmd(add_to_pic_f) {
- g_sprites->addToPic(_v[p0], _v[p1], _v[p2], _v[p3], _v[p4], _v[p5], _v[p6]);
+void AgiEngine::cmd_add_to_pic_f(uint8 *p) {
+ _sprites->addToPic(_v[p0], _v[p1], _v[p2], _v[p3], _v[p4], _v[p5], _v[p6]);
}
-cmd(force_update) {
- g_sprites->eraseBoth();
- g_sprites->blitBoth();
- g_sprites->commitBoth();
+void AgiEngine::cmd_force_update(uint8 *p) {
+ _sprites->eraseBoth();
+ _sprites->blitBoth();
+ _sprites->commitBoth();
}
-cmd(reverse_loop) {
+void AgiEngine::cmd_reverse_loop(uint8 *p) {
debugC(4, kDebugLevelScripts, "o%d, f%d", p0, p1);
vt.cycle = CYCLE_REV_LOOP;
vt.flags |= (DONTUPDATE | UPDATE | CYCLING);
vt.parm1 = p1;
- g_agi->setflag(p1, false);
+ setflag(p1, false);
}
-cmd(end_of_loop) {
+void AgiEngine::cmd_end_of_loop(uint8 *p) {
debugC(4, kDebugLevelScripts, "o%d, f%d", p0, p1);
vt.cycle = CYCLE_END_OF_LOOP;
vt.flags |= (DONTUPDATE | UPDATE | CYCLING);
vt.parm1 = p1;
- g_agi->setflag(p1, false);
+ setflag(p1, false);
}
-cmd(block) {
+void AgiEngine::cmd_block(uint8 *p) {
debugC(4, kDebugLevelScripts, "x1=%d, y1=%d, x2=%d, y2=%d", p0, p1, p2, p3);
- game.block.active = true;
- game.block.x1 = p0;
- game.block.y1 = p1;
- game.block.x2 = p2;
- game.block.y2 = p3;
+ _game.block.active = true;
+ _game.block.x1 = p0;
+ _game.block.y1 = p1;
+ _game.block.x2 = p2;
+ _game.block.y2 = p3;
}
-cmd(unblock) {
- game.block.active = false;
+void AgiEngine::cmd_unblock(uint8 *p) {
+ _game.block.active = false;
}
-cmd(normal_motion) {
+void AgiEngine::cmd_normal_motion(uint8 *p) {
vt.motion = MOTION_NORMAL;
}
-cmd(stop_motion) {
+void AgiEngine::cmd_stop_motion(uint8 *p) {
vt.direction = 0;
vt.motion = MOTION_NORMAL;
if (p0 == 0) { // ego only
_v[vEgoDir] = 0;
- game.playerControl = false;
+ _game.playerControl = false;
}
}
-cmd(start_motion) {
+void AgiEngine::cmd_start_motion(uint8 *p) {
vt.motion = MOTION_NORMAL;
if (p0 == 0) { // ego only
_v[vEgoDir] = 0;
- game.playerControl = true;
+ _game.playerControl = true;
}
}
-cmd(player_control) {
- game.playerControl = true;
- game.viewTable[0].motion = MOTION_NORMAL;
+void AgiEngine::cmd_player_control(uint8 *p) {
+ _game.playerControl = true;
+ _game.viewTable[0].motion = MOTION_NORMAL;
}
-cmd(program_control) {
- game.playerControl = false;
+void AgiEngine::cmd_program_control(uint8 *p) {
+ _game.playerControl = false;
}
-cmd(follow_ego) {
+void AgiEngine::cmd_follow_ego(uint8 *p) {
vt.motion = MOTION_FOLLOW_EGO;
vt.parm1 = p1 > vt.stepSize ? p1 : vt.stepSize;
vt.parm2 = p2;
vt.parm3 = 0xff;
- g_agi->setflag(p2, false);
+ setflag(p2, false);
vt.flags |= UPDATE;
}
-cmd(move_obj) {
+void AgiEngine::cmd_move_obj(uint8 *p) {
// _D (_D_WARN "o=%d, x=%d, y=%d, s=%d, f=%d", p0, p1, p2, p3, p4);
vt.motion = MOTION_MOVE_OBJ;
@@ -1068,18 +1054,18 @@ cmd(move_obj) {
if (p3 != 0)
vt.stepSize = p3;
- g_agi->setflag(p4, false);
+ setflag(p4, false);
vt.flags |= UPDATE;
if (p0 == 0)
- game.playerControl = false;
+ _game.playerControl = false;
// AGI 2.272 (ddp, xmas) doesn't call move_obj!
- if (g_agi->getVersion() > 0x2272)
- g_agi->moveObj(&vt);
+ if (getVersion() > 0x2272)
+ moveObj(&vt);
}
-cmd(move_obj_f) {
+void AgiEngine::cmd_move_obj_f(uint8 *p) {
vt.motion = MOTION_MOVE_OBJ;
vt.parm1 = _v[p1];
vt.parm2 = _v[p2];
@@ -1089,67 +1075,67 @@ cmd(move_obj_f) {
if (_v[p3] != 0)
vt.stepSize = _v[p3];
- g_agi->setflag(p4, false);
+ setflag(p4, false);
vt.flags |= UPDATE;
if (p0 == 0)
- game.playerControl = false;
+ _game.playerControl = false;
// AGI 2.272 (ddp, xmas) doesn't call move_obj!
- if (g_agi->getVersion() > 0x2272)
- g_agi->moveObj(&vt);
+ if (getVersion() > 0x2272)
+ moveObj(&vt);
}
-cmd(wander) {
+void AgiEngine::cmd_wander(uint8 *p) {
if (p0 == 0)
- game.playerControl = false;
+ _game.playerControl = false;
vt.motion = MOTION_WANDER;
vt.flags |= UPDATE;
}
-cmd(set_game_id) {
- if (curLogic->texts && (p0 - 1) <= curLogic->numTexts)
- strncpy(game.id, curLogic->texts[p0 - 1], 8);
+void AgiEngine::cmd_set_game_id(uint8 *p) {
+ if (_curLogic->texts && (p0 - 1) <= _curLogic->numTexts)
+ strncpy(_game.id, _curLogic->texts[p0 - 1], 8);
else
- game.id[0] = 0;
+ _game.id[0] = 0;
- report("Game ID: \"%s\"\n", game.id);
+ report("Game ID: \"%s\"\n", _game.id);
}
-cmd(pause) {
- int tmp = game.clockEnabled;
+void AgiEngine::cmd_pause(uint8 *p) {
+ int tmp = _game.clockEnabled;
const char *b[] = { "Continue", NULL };
const char *b_ru[] = { "\x8f\xe0\xae\xa4\xae\xab\xa6\xa8\xe2\xec", NULL };
- game.clockEnabled = false;
+ _game.clockEnabled = false;
- switch (g_agi->getLanguage()) {
+ switch (getLanguage()) {
case Common::RU_RUS:
- g_agi->selectionBox(" \x88\xa3\xe0\xa0 \xae\xe1\xe2\xa0\xad\xae\xa2\xab\xa5\xad\xa0. \n\n\n", b_ru);
+ selectionBox(" \x88\xa3\xe0\xa0 \xae\xe1\xe2\xa0\xad\xae\xa2\xab\xa5\xad\xa0. \n\n\n", b_ru);
break;
default:
- g_agi->selectionBox(" Game is paused. \n\n\n", b);
+ selectionBox(" Game is paused. \n\n\n", b);
break;
}
- game.clockEnabled = tmp;
+ _game.clockEnabled = tmp;
}
-cmd(set_menu) {
- debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, curLogic->numTexts);
+void AgiEngine::cmd_set_menu(uint8 *p) {
+ debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, _curLogic->numTexts);
- if (curLogic->texts != NULL && p0 <= curLogic->numTexts)
- g_agi->_menu->add(curLogic->texts[p0 - 1]);
+ if (_curLogic->texts != NULL && p0 <= _curLogic->numTexts)
+ _menu->add(_curLogic->texts[p0 - 1]);
}
-cmd(set_menu_item) {
- debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, curLogic->numTexts);
+void AgiEngine::cmd_set_menu_item(uint8 *p) {
+ debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, _curLogic->numTexts);
- if (curLogic->texts != NULL && p0 <= curLogic->numTexts)
- g_agi->_menu->addItem(curLogic->texts[p0 - 1], p1);
+ if (_curLogic->texts != NULL && p0 <= _curLogic->numTexts)
+ _menu->addItem(_curLogic->texts[p0 - 1], p1);
}
-cmd(version) {
+void AgiEngine::cmd_version(uint8 *p) {
char verMsg[64];
char ver2Msg[] =
"\n"
@@ -1168,7 +1154,7 @@ cmd(version) {
sprintf(verMsg, TITLE " v%s", gScummVMVersion);
- ver = g_agi->getVersion();
+ ver = getVersion();
maj = (ver >> 12) & 0xf;
min = ver & 0xfff;
@@ -1186,88 +1172,87 @@ cmd(version) {
strncpy(q + 1 + gap, verMsg, strlen(verMsg));
sprintf(msg, q, maj, min);
- g_agi->messageBox(msg);
+ messageBox(msg);
}
-cmd(configure_screen) {
- game.lineMinPrint = p0;
- game.lineUserInput = p1;
- game.lineStatus = p2;
+void AgiEngine::cmd_configure_screen(uint8 *p) {
+ _game.lineMinPrint = p0;
+ _game.lineUserInput = p1;
+ _game.lineStatus = p2;
}
-cmd(text_screen) {
+void AgiEngine::cmd_text_screen(uint8 *p) {
debugC(4, kDebugLevelScripts, "switching to text mode");
- game.gfxMode = false;
+ _game.gfxMode = false;
// Simulates the "bright background bit" of the PC video
// controller.
- if (game.colorBg)
- game.colorBg |= 0x08;
+ if (_game.colorBg)
+ _game.colorBg |= 0x08;
- g_gfx->clearScreen(game.colorBg);
+ _gfx->clearScreen(_game.colorBg);
}
-cmd(graphics) {
+void AgiEngine::cmd_graphics(uint8 *p) {
debugC(4, kDebugLevelScripts, "switching to graphics mode");
- if (!game.gfxMode) {
- game.gfxMode = true;
- g_gfx->clearScreen(0);
- g_picture->showPic();
- g_agi->writeStatus();
- g_agi->writePrompt();
+ if (!_game.gfxMode) {
+ _game.gfxMode = true;
+ _gfx->clearScreen(0);
+ _picture->showPic();
+ writeStatus();
+ writePrompt();
}
}
-cmd(set_text_attribute) {
- game.colorFg = p0;
- game.colorBg = p1;
+void AgiEngine::cmd_set_text_attribute(uint8 *p) {
+ _game.colorFg = p0;
+ _game.colorBg = p1;
- if (game.gfxMode) {
- if (game.colorBg != 0) {
- game.colorFg = 0;
- game.colorBg = 15;
+ if (_game.gfxMode) {
+ if (_game.colorBg != 0) {
+ _game.colorFg = 0;
+ _game.colorBg = 15;
}
}
}
-cmd(status) {
- g_agi->inventory();
+void AgiEngine::cmd_status(uint8 *p) {
+ inventory();
}
-cmd(quit) {
+void AgiEngine::cmd_quit(uint8 *p) {
const char *buttons[] = { "Quit", "Continue", NULL };
- g_sound->stopSound();
+ _sound->stopSound();
if (p0) {
- g_agi->quitGame();
+ quitGame();
} else {
- if (g_agi->selectionBox
- (" Quit the game, or continue? \n\n\n", buttons) == 0) {
- g_agi->quitGame();
+ if (selectionBox(" Quit the game, or continue? \n\n\n", buttons) == 0) {
+ quitGame();
}
}
}
-cmd(restart_game) {
+void AgiEngine::cmd_restart_game(uint8 *p) {
const char *buttons[] = { "Restart", "Continue", NULL };
int sel;
- g_sound->stopSound();
- sel = g_agi->getflag(fAutoRestart) ? 0 :
- g_agi->selectionBox(" Restart game, or continue? \n\n\n", buttons);
+ _sound->stopSound();
+ sel = getflag(fAutoRestart) ? 0 :
+ selectionBox(" Restart _game, or continue? \n\n\n", buttons);
if (sel == 0) {
- g_agi->_restartGame = true;
- g_agi->setflag(fRestartGame, true);
- g_agi->_menu->enableAll();
+ _restartGame = true;
+ setflag(fRestartGame, true);
+ _menu->enableAll();
}
}
-cmd(distance) {
+void AgiEngine::cmd_distance(uint8 *p) {
int16 x1, y1, x2, y2, d;
- VtEntry *v0 = &game.viewTable[p0];
- VtEntry *v1 = &game.viewTable[p1];
+ VtEntry *v0 = &_game.viewTable[p0];
+ VtEntry *v1 = &_game.viewTable[p1];
if (v0->flags & DRAWN && v1->flags & DRAWN) {
x1 = v0->xPos + v0->xSize / 2;
@@ -1290,7 +1275,7 @@ cmd(distance) {
// wouldn't chase Rosella around anymore. If it had worked correctly the zombie
// wouldn't have come up at all or it would have come up and gone back down
// immediately. The latter approach is the one implemented here.
- if (g_agi->getGameID() == GID_KQ4 && (_v[vCurRoom] == 16 || _v[vCurRoom] == 18) && p2 >= 221 && p2 <= 223) {
+ if (getGameID() == GID_KQ4 && (_v[vCurRoom] == 16 || _v[vCurRoom] == 18) && p2 >= 221 && p2 <= 223) {
// Rooms 16 and 18 are graveyards where three zombies come up at night. They use logics 16 and 18.
// Variables 221-223 are used to save the distance between each zombie and Rosella.
// Variables 155, 156 and 162 are used to save the state of each zombie in room 16.
@@ -1315,24 +1300,24 @@ cmd(distance) {
_v[p2] = (unsigned char)d;
}
-cmd(accept_input) {
+void AgiEngine::cmd_accept_input(uint8 *p) {
debugC(4, kDebugLevelScripts | kDebugLevelInput, "input normal");
- g_agi->newInputMode(INPUT_NORMAL);
- game.inputEnabled = true;
- g_agi->writePrompt();
+ newInputMode(INPUT_NORMAL);
+ _game.inputEnabled = true;
+ writePrompt();
}
-cmd(prevent_input) {
+void AgiEngine::cmd_prevent_input(uint8 *p) {
debugC(4, kDebugLevelScripts | kDebugLevelInput, "no input");
- g_agi->newInputMode(INPUT_NONE);
- game.inputEnabled = false;
+ newInputMode(INPUT_NONE);
+ _game.inputEnabled = false;
- g_agi->clearPrompt();
+ clearPrompt();
}
-cmd(get_string) {
+void AgiEngine::cmd_get_string(uint8 *p) {
int tex, row, col;
debugC(4, kDebugLevelScripts, "%d %d %d %d %d", p0, p1, p2, p3, p4);
@@ -1348,63 +1333,63 @@ cmd(get_string) {
if (col > 39)
col = 39;
- g_agi->newInputMode(INPUT_GETSTRING);
+ newInputMode(INPUT_GETSTRING);
- if (curLogic->texts != NULL && curLogic->numTexts >= tex) {
- int len = strlen(curLogic->texts[tex]);
+ if (_curLogic->texts != NULL && _curLogic->numTexts >= tex) {
+ int len = strlen(_curLogic->texts[tex]);
- g_agi->printText(curLogic->texts[tex], 0, col, row, len, game.colorFg, game.colorBg);
- g_agi->getString(col + len - 1, row, p4, p0);
+ printText(_curLogic->texts[tex], 0, col, row, len, _game.colorFg, _game.colorBg);
+ getString(col + len - 1, row, p4, p0);
// SGEO: display input char
- g_gfx->printCharacter((col + len), row, game.cursorChar, game.colorFg, game.colorBg);
+ _gfx->printCharacter((col + len), row, _game.cursorChar, _game.colorFg, _game.colorBg);
}
do {
- g_agi->mainCycle();
- } while (game.inputMode == INPUT_GETSTRING && !(g_agi->shouldQuit() || g_agi->_restartGame));
+ mainCycle();
+ } while (_game.inputMode == INPUT_GETSTRING && !(shouldQuit() || _restartGame));
}
-cmd(get_num) {
+void AgiEngine::cmd_get_num(uint8 *p) {
debugC(4, kDebugLevelScripts, "%d %d", p0, p1);
- g_agi->newInputMode(INPUT_GETSTRING);
+ newInputMode(INPUT_GETSTRING);
- if (curLogic->texts != NULL && curLogic->numTexts >= (p0 - 1)) {
- int len = strlen(curLogic->texts[p0 - 1]);
+ if (_curLogic->texts != NULL && _curLogic->numTexts >= (p0 - 1)) {
+ int len = strlen(_curLogic->texts[p0 - 1]);
- g_agi->printText(curLogic->texts[p0 - 1], 0, 0, 22, len, game.colorFg, game.colorBg);
- g_agi->getString(len - 1, 22, 3, MAX_STRINGS);
+ printText(_curLogic->texts[p0 - 1], 0, 0, 22, len, _game.colorFg, _game.colorBg);
+ getString(len - 1, 22, 3, MAX_STRINGS);
// CM: display input char
- g_gfx->printCharacter((p3 + len), 22, game.cursorChar, game.colorFg, game.colorBg);
+ _gfx->printCharacter((p3 + len), 22, _game.cursorChar, _game.colorFg, _game.colorBg);
}
do {
- g_agi->mainCycle();
- } while (game.inputMode == INPUT_GETSTRING && !(g_agi->shouldQuit() || g_agi->_restartGame));
+ mainCycle();
+ } while (_game.inputMode == INPUT_GETSTRING && !(shouldQuit() || _restartGame));
- _v[p1] = atoi(game.strings[MAX_STRINGS]);
+ _v[p1] = atoi(_game.strings[MAX_STRINGS]);
- debugC(4, kDebugLevelScripts, "[%s] -> %d", game.strings[MAX_STRINGS], _v[p1]);
+ debugC(4, kDebugLevelScripts, "[%s] -> %d", _game.strings[MAX_STRINGS], _v[p1]);
- g_agi->clearLines(22, 22, game.colorBg);
- g_agi->flushLines(22, 22);
+ clearLines(22, 22, _game.colorBg);
+ flushLines(22, 22);
}
-cmd(set_cursor_char) {
- if (curLogic->texts != NULL && (p0 - 1) <= curLogic->numTexts) {
- game.cursorChar = *curLogic->texts[p0 - 1];
+void AgiEngine::cmd_set_cursor_char(uint8 *p) {
+ if (_curLogic->texts != NULL && (p0 - 1) <= _curLogic->numTexts) {
+ _game.cursorChar = *_curLogic->texts[p0 - 1];
} else {
// default
- game.cursorChar = '_';
+ _game.cursorChar = '_';
}
}
-cmd(set_key) {
+void AgiEngine::cmd_set_key(uint8 *p) {
int key;
- if (game.lastController >= MAX_CONTROLLERS) {
+ if (_game.lastController >= MAX_CONTROLLERS) {
warning("Number of set.keys exceeded %d", MAX_CONTROLLERS);
return;
}
@@ -1413,36 +1398,35 @@ cmd(set_key) {
key = 256 * p1 + p0;
- game.controllers[game.lastController].keycode = key;
- game.controllers[game.lastController].controller = p2;
- game.lastController++;
+ _game.controllers[_game.lastController].keycode = key;
+ _game.controllers[_game.lastController].controller = p2;
+ _game.lastController++;
- game.controllerOccured[p2] = false;
+ _game.controllerOccured[p2] = false;
}
-cmd(set_string) {
+void AgiEngine::cmd_set_string(uint8 *p) {
// CM: to avoid crash in Groza (str = 150)
if (p0 > MAX_STRINGS)
return;
- strcpy(game.strings[p0], curLogic->texts[p1 - 1]);
+ strcpy(_game.strings[p0], _curLogic->texts[p1 - 1]);
}
-cmd(display) {
+void AgiEngine::cmd_display(uint8 *p) {
int len = 40;
- char *s = g_agi->wordWrapString(curLogic->texts[p2 - 1], &len);
+ char *s = wordWrapString(_curLogic->texts[p2 - 1], &len);
- g_agi->printText(s, p1, 0, p0, 40, game.colorFg, game.colorBg);
+ printText(s, p1, 0, p0, 40, _game.colorFg, _game.colorBg);
free(s);
}
-cmd(display_f) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- g_agi->printText(curLogic->texts[_v[p2] - 1], _v[p1], 0, _v[p0], 40, game.colorFg, game.colorBg);
+void AgiEngine::cmd_display_f(uint8 *p) {
+ printText(_curLogic->texts[_v[p2] - 1], _v[p1], 0, _v[p0], 40, _game.colorFg, _game.colorBg);
}
-cmd(clear_text_rect) {
+void AgiEngine::cmd_clear_text_rect(uint8 *p) {
int c, x1, y1, x2, y2;
if ((c = p4) != 0)
@@ -1463,21 +1447,21 @@ cmd(clear_text_rect) {
if (y2 > GFX_HEIGHT)
y2 = GFX_HEIGHT - 1;
- g_gfx->drawRectangle(x1, y1, x2, y2, c);
- g_gfx->flushBlock(x1, y1, x2, y2);
+ _gfx->drawRectangle(x1, y1, x2, y2, c);
+ _gfx->flushBlock(x1, y1, x2, y2);
}
-cmd(toggle_monitor) {
+void AgiEngine::cmd_toggle_monitor(uint8 *p) {
report("toggle.monitor\n");
}
-cmd(echo_line) {
- strcpy((char *)game.inputBuffer, (const char *)game.echoBuffer);
- game.cursorPos = strlen((char *)game.inputBuffer);
- game.hasPrompt = 0;
+void AgiEngine::cmd_echo_line(uint8 *p) {
+ strcpy((char *)_game.inputBuffer, (const char *)_game.echoBuffer);
+ _game.cursorPos = strlen((char *)_game.inputBuffer);
+ _game.hasPrompt = 0;
}
-cmd(clear_lines) {
+void AgiEngine::cmd_clear_lines(uint8 *p) {
uint8 l;
// Residence 44 calls clear.lines(24,0,0), see Sarien bug #558423
@@ -1487,78 +1471,78 @@ cmd(clear_lines) {
// #1935838 and #1935842
l = (l <= 24) ? l : 24;
- g_agi->clearLines(p0, l, p2);
- g_agi->flushLines(p0, l);
+ clearLines(p0, l, p2);
+ flushLines(p0, l);
}
-cmd(print) {
+void AgiEngine::cmd_print(uint8 *p) {
int n = p0 < 1 ? 1 : p0;
- g_agi->print(curLogic->texts[n - 1], 0, 0, 0);
+ print(_curLogic->texts[n - 1], 0, 0, 0);
}
-cmd(print_f) {
+void AgiEngine::cmd_print_f(uint8 *p) {
int n = _v[p0] < 1 ? 1 : _v[p0];
- g_agi->print(curLogic->texts[n - 1], 0, 0, 0);
+ print(_curLogic->texts[n - 1], 0, 0, 0);
}
-cmd(print_at) {
+void AgiEngine::cmd_print_at(uint8 *p) {
int n = p0 < 1 ? 1 : p0;
debugC(4, kDebugLevelScripts, "%d %d %d %d", p0, p1, p2, p3);
- g_agi->print(curLogic->texts[n - 1], p1, p2, p3);
+ print(_curLogic->texts[n - 1], p1, p2, p3);
}
-cmd(print_at_v) {
+void AgiEngine::cmd_print_at_v(uint8 *p) {
int n = _v[p0] < 1 ? 1 : _v[p0];
- g_agi->print(curLogic->texts[n - 1], p1, p2, p3);
+ print(_curLogic->texts[n - 1], p1, p2, p3);
}
-cmd(push_script) {
+void AgiEngine::cmd_push_script(uint8 *p) {
// We run AGIMOUSE always as a side effect
- if (g_agi->getFeatures() & GF_AGIMOUSE || 1) {
- game.vars[27] = g_agi->_mouse.button;
- game.vars[28] = g_agi->_mouse.x / 2;
- game.vars[29] = g_agi->_mouse.y;
+ if (getFeatures() & GF_AGIMOUSE || true) {
+ _game.vars[27] = _mouse.button;
+ _game.vars[28] = _mouse.x / 2;
+ _game.vars[29] = _mouse.y;
} else {
- if (g_agi->getVersion() >= 0x2915) {
+ if (getVersion() >= 0x2915) {
report("push.script\n");
}
}
}
-cmd(set_pri_base) {
+void AgiEngine::cmd_set_pri_base(uint8 *p) {
int i, x, pri;
report("Priority base set to %d\n", p0);
- // game.alt_pri = true;
+ // _game.alt_pri = true;
x = (_HEIGHT - p0) * _HEIGHT / 10;
for (i = 0; i < _HEIGHT; i++) {
pri = (i - p0) < 0 ? 4 : (i - p0) * _HEIGHT / x + 5;
if (pri > 15)
pri = 15;
- game.priTable[i] = pri;
+ _game.priTable[i] = pri;
}
}
-cmd(mouse_posn) {
- _v[p0] = WIN_TO_PIC_X(g_agi->_mouse.x);
- _v[p1] = WIN_TO_PIC_Y(g_agi->_mouse.y);
+void AgiEngine::cmd_mouse_posn(uint8 *p) {
+ _v[p0] = WIN_TO_PIC_X(_mouse.x);
+ _v[p1] = WIN_TO_PIC_Y(_mouse.y);
}
-cmd(shake_screen) {
+void AgiEngine::cmd_shake_screen(uint8 *p) {
int i;
// AGIPAL uses shake.screen values between 100 and 109 to set the palette
// (Checked the original AGIPAL-hack's shake.screen-routine's disassembly).
if (p0 >= 100 && p0 < 110) {
- if (g_agi->getFeatures() & GF_AGIPAL) {
- g_gfx->setAGIPal(p0);
+ if (getFeatures() & GF_AGIPAL) {
+ _gfx->setAGIPal(p0);
return;
} else {
warning("It looks like GF_AGIPAL flag is missing");
@@ -1567,208 +1551,213 @@ cmd(shake_screen) {
// Disables input while shaking to prevent bug
// #1678230: AGI: Entering text while screen is shaking
- int originalValue = game.inputEnabled;
- game.inputEnabled = 0;
+ bool originalValue = _game.inputEnabled;
+ _game.inputEnabled = false;
- g_gfx->shakeStart();
+ _gfx->shakeStart();
- g_sprites->commitBoth(); // Fixes SQ1 demo
+ _sprites->commitBoth(); // Fixes SQ1 demo
for (i = 4 * p0; i; i--) {
- g_gfx->shakeScreen(i & 1);
- g_gfx->flushBlock(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
- g_agi->mainCycle();
+ _gfx->shakeScreen(i & 1);
+ _gfx->flushBlock(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
+ mainCycle();
}
- g_gfx->shakeEnd();
+ _gfx->shakeEnd();
// Sets input back to what it was
- game.inputEnabled = originalValue;
-}
-
-static void (*agiCommand[183])(AgiEngine *, uint8 *) = {
- NULL, // 0x00
- cmd_increment,
- cmd_decrement,
- cmd_assignn,
- cmd_assignv,
- cmd_addn,
- cmd_addv,
- cmd_subn,
- cmd_subv, // 0x08
- cmd_lindirectv,
- cmd_rindirect,
- cmd_lindirectn,
- cmd_set,
- cmd_reset,
- cmd_toggle,
- cmd_set_v,
- cmd_reset_v, // 0x10
- cmd_toggle_v,
- cmd_new_room,
- cmd_new_room_f,
- cmd_load_logic,
- cmd_load_logic_f,
- cmd_call,
- cmd_call_f,
- cmd_load_pic, // 0x18
- cmd_draw_pic,
- cmd_show_pic,
- cmd_discard_pic,
- cmd_overlay_pic,
- cmd_show_pri_screen,
- cmd_load_view,
- cmd_load_view_f,
- cmd_discard_view, // 0x20
- cmd_animate_obj,
- cmd_unanimate_all,
- cmd_draw,
- cmd_erase,
- cmd_position,
- cmd_position_f,
- cmd_get_posn,
- cmd_reposition, // 0x28
- cmd_set_view,
- cmd_set_view_f,
- cmd_set_loop,
- cmd_set_loop_f,
- cmd_fix_loop,
- cmd_release_loop,
- cmd_set_cel,
- cmd_set_cel_f, // 0x30
- cmd_last_cel,
- cmd_current_cel,
- cmd_current_loop,
- cmd_current_view,
- cmd_number_of_loops,
- cmd_set_priority,
- cmd_set_priority_f,
- cmd_release_priority, // 0x38
- cmd_get_priority,
- cmd_stop_update,
- cmd_start_update,
- cmd_force_update,
- cmd_ignore_horizon,
- cmd_observe_horizon,
- cmd_set_horizon,
- cmd_object_on_water, // 0x40
- cmd_object_on_land,
- cmd_object_on_anything,
- cmd_ignore_objs,
- cmd_observe_objs,
- cmd_distance,
- cmd_stop_cycling,
- cmd_start_cycling,
- cmd_normal_cycle, // 0x48
- cmd_end_of_loop,
- cmd_reverse_cycle,
- cmd_reverse_loop,
- cmd_cycle_time,
- cmd_stop_motion,
- cmd_start_motion,
- cmd_step_size,
- cmd_step_time, // 0x50
- cmd_move_obj,
- cmd_move_obj_f,
- cmd_follow_ego,
- cmd_wander,
- cmd_normal_motion,
- cmd_set_dir,
- cmd_get_dir,
- cmd_ignore_blocks, // 0x58
- cmd_observe_blocks,
- cmd_block,
- cmd_unblock,
- cmd_get,
- cmd_get_f,
- cmd_drop,
- cmd_put,
- cmd_put_f, // 0x60
- cmd_get_room_f,
- cmd_load_sound,
- cmd_sound,
- cmd_stop_sound,
- cmd_print,
- cmd_print_f,
- cmd_display,
- cmd_display_f, // 0x68
- cmd_clear_lines,
- cmd_text_screen,
- cmd_graphics,
- cmd_set_cursor_char,
- cmd_set_text_attribute,
- cmd_shake_screen,
- cmd_configure_screen,
- cmd_status_line_on, // 0x70
- cmd_status_line_off,
- cmd_set_string,
- cmd_get_string,
- cmd_word_to_string,
- cmd_parse,
- cmd_get_num,
- cmd_prevent_input,
- cmd_accept_input, // 0x78
- cmd_set_key,
- cmd_add_to_pic,
- cmd_add_to_pic_f,
- cmd_status,
- cmd_save_game,
- cmd_load_game,
- cmd_init_disk,
- cmd_restart_game, // 0x80
- cmd_show_obj,
- cmd_random,
- cmd_program_control,
- cmd_player_control,
- cmd_obj_status_f,
- cmd_quit,
- cmd_show_mem,
- cmd_pause, // 0x88
- cmd_echo_line,
- cmd_cancel_line,
- cmd_init_joy,
- cmd_toggle_monitor,
- cmd_version,
- cmd_script_size,
- cmd_set_game_id,
- cmd_log, // 0x90
- cmd_set_scan_start,
- cmd_reset_scan_start,
- cmd_reposition_to,
- cmd_reposition_to_f,
- cmd_trace_on,
- cmd_trace_info,
- cmd_print_at,
- cmd_print_at_v, // 0x98
- cmd_discard_view,
- cmd_clear_text_rect,
- cmd_set_upper_left,
- cmd_set_menu,
- cmd_set_menu_item,
- cmd_submit_menu,
- cmd_enable_item,
- cmd_disable_item, // 0xa0
- cmd_menu_input,
- cmd_show_obj_v,
- cmd_open_dialogue,
- cmd_close_dialogue,
- cmd_mul_n,
- cmd_mul_v,
- cmd_div_n,
- cmd_div_v, // 0xa8
- cmd_close_window,
- cmd_set_simple,
- cmd_push_script,
- cmd_pop_script,
- cmd_hold_key,
- cmd_set_pri_base,
- cmd_discard_sound,
- cmd_hide_mouse, // 0xb0
- cmd_allow_menu,
- cmd_show_mouse,
- cmd_fence_mouse,
- cmd_mouse_posn,
- cmd_release_key,
- cmd_adj_ego_move_to_x_y
-};
+ _game.inputEnabled = originalValue;
+}
+
+void AgiEngine::setupOpcodes() {
+ AgiCommand tmp[] = {
+ NULL, // 0x00
+ &AgiEngine::cmd_increment,
+ &AgiEngine::cmd_decrement,
+ &AgiEngine::cmd_assignn,
+ &AgiEngine::cmd_assignv,
+ &AgiEngine::cmd_addn,
+ &AgiEngine::cmd_addv,
+ &AgiEngine::cmd_subn,
+ &AgiEngine::cmd_subv, // 0x08
+ &AgiEngine::cmd_lindirectv,
+ &AgiEngine::cmd_rindirect,
+ &AgiEngine::cmd_lindirectn,
+ &AgiEngine::cmd_set,
+ &AgiEngine::cmd_reset,
+ &AgiEngine::cmd_toggle,
+ &AgiEngine::cmd_set_v,
+ &AgiEngine::cmd_reset_v, // 0x10
+ &AgiEngine::cmd_toggle_v,
+ &AgiEngine::cmd_new_room,
+ &AgiEngine::cmd_new_room_f,
+ &AgiEngine::cmd_load_logic,
+ &AgiEngine::cmd_load_logic_f,
+ &AgiEngine::cmd_call,
+ &AgiEngine::cmd_call_f,
+ &AgiEngine::cmd_load_pic, // 0x18
+ &AgiEngine::cmd_draw_pic,
+ &AgiEngine::cmd_show_pic,
+ &AgiEngine::cmd_discard_pic,
+ &AgiEngine::cmd_overlay_pic,
+ &AgiEngine::cmd_show_pri_screen,
+ &AgiEngine::cmd_load_view,
+ &AgiEngine::cmd_load_view_f,
+ &AgiEngine::cmd_discard_view, // 0x20
+ &AgiEngine::cmd_animate_obj,
+ &AgiEngine::cmd_unanimate_all,
+ &AgiEngine::cmd_draw,
+ &AgiEngine::cmd_erase,
+ &AgiEngine::cmd_position,
+ &AgiEngine::cmd_position_f,
+ &AgiEngine::cmd_get_posn,
+ &AgiEngine::cmd_reposition, // 0x28
+ &AgiEngine::cmd_set_view,
+ &AgiEngine::cmd_set_view_f,
+ &AgiEngine::cmd_set_loop,
+ &AgiEngine::cmd_set_loop_f,
+ &AgiEngine::cmd_fix_loop,
+ &AgiEngine::cmd_release_loop,
+ &AgiEngine::cmd_set_cel,
+ &AgiEngine::cmd_set_cel_f, // 0x30
+ &AgiEngine::cmd_last_cel,
+ &AgiEngine::cmd_current_cel,
+ &AgiEngine::cmd_current_loop,
+ &AgiEngine::cmd_current_view,
+ &AgiEngine::cmd_number_of_loops,
+ &AgiEngine::cmd_set_priority,
+ &AgiEngine::cmd_set_priority_f,
+ &AgiEngine::cmd_release_priority, // 0x38
+ &AgiEngine::cmd_get_priority,
+ &AgiEngine::cmd_stop_update,
+ &AgiEngine::cmd_start_update,
+ &AgiEngine::cmd_force_update,
+ &AgiEngine::cmd_ignore_horizon,
+ &AgiEngine::cmd_observe_horizon,
+ &AgiEngine::cmd_set_horizon,
+ &AgiEngine::cmd_object_on_water, // 0x40
+ &AgiEngine::cmd_object_on_land,
+ &AgiEngine::cmd_object_on_anything,
+ &AgiEngine::cmd_ignore_objs,
+ &AgiEngine::cmd_observe_objs,
+ &AgiEngine::cmd_distance,
+ &AgiEngine::cmd_stop_cycling,
+ &AgiEngine::cmd_start_cycling,
+ &AgiEngine::cmd_normal_cycle, // 0x48
+ &AgiEngine::cmd_end_of_loop,
+ &AgiEngine::cmd_reverse_cycle,
+ &AgiEngine::cmd_reverse_loop,
+ &AgiEngine::cmd_cycle_time,
+ &AgiEngine::cmd_stop_motion,
+ &AgiEngine::cmd_start_motion,
+ &AgiEngine::cmd_step_size,
+ &AgiEngine::cmd_step_time, // 0x50
+ &AgiEngine::cmd_move_obj,
+ &AgiEngine::cmd_move_obj_f,
+ &AgiEngine::cmd_follow_ego,
+ &AgiEngine::cmd_wander,
+ &AgiEngine::cmd_normal_motion,
+ &AgiEngine::cmd_set_dir,
+ &AgiEngine::cmd_get_dir,
+ &AgiEngine::cmd_ignore_blocks, // 0x58
+ &AgiEngine::cmd_observe_blocks,
+ &AgiEngine::cmd_block,
+ &AgiEngine::cmd_unblock,
+ &AgiEngine::cmd_get,
+ &AgiEngine::cmd_get_f,
+ &AgiEngine::cmd_drop,
+ &AgiEngine::cmd_put,
+ &AgiEngine::cmd_put_f, // 0x60
+ &AgiEngine::cmd_get_room_f,
+ &AgiEngine::cmd_load_sound,
+ &AgiEngine::cmd_sound,
+ &AgiEngine::cmd_stop_sound,
+ &AgiEngine::cmd_print,
+ &AgiEngine::cmd_print_f,
+ &AgiEngine::cmd_display,
+ &AgiEngine::cmd_display_f, // 0x68
+ &AgiEngine::cmd_clear_lines,
+ &AgiEngine::cmd_text_screen,
+ &AgiEngine::cmd_graphics,
+ &AgiEngine::cmd_set_cursor_char,
+ &AgiEngine::cmd_set_text_attribute,
+ &AgiEngine::cmd_shake_screen,
+ &AgiEngine::cmd_configure_screen,
+ &AgiEngine::cmd_status_line_on, // 0x70
+ &AgiEngine::cmd_status_line_off,
+ &AgiEngine::cmd_set_string,
+ &AgiEngine::cmd_get_string,
+ &AgiEngine::cmd_word_to_string,
+ &AgiEngine::cmd_parse,
+ &AgiEngine::cmd_get_num,
+ &AgiEngine::cmd_prevent_input,
+ &AgiEngine::cmd_accept_input, // 0x78
+ &AgiEngine::cmd_set_key,
+ &AgiEngine::cmd_add_to_pic,
+ &AgiEngine::cmd_add_to_pic_f,
+ &AgiEngine::cmd_status,
+ &AgiEngine::cmd_save_game,
+ &AgiEngine::cmd_load_game,
+ &AgiEngine::cmd_init_disk,
+ &AgiEngine::cmd_restart_game, // 0x80
+ &AgiEngine::cmd_show_obj,
+ &AgiEngine::cmd_random,
+ &AgiEngine::cmd_program_control,
+ &AgiEngine::cmd_player_control,
+ &AgiEngine::cmd_obj_status_f,
+ &AgiEngine::cmd_quit,
+ &AgiEngine::cmd_show_mem,
+ &AgiEngine::cmd_pause, // 0x88
+ &AgiEngine::cmd_echo_line,
+ &AgiEngine::cmd_cancel_line,
+ &AgiEngine::cmd_init_joy,
+ &AgiEngine::cmd_toggle_monitor,
+ &AgiEngine::cmd_version,
+ &AgiEngine::cmd_script_size,
+ &AgiEngine::cmd_set_game_id,
+ &AgiEngine::cmd_log, // 0x90
+ &AgiEngine::cmd_set_scan_start,
+ &AgiEngine::cmd_reset_scan_start,
+ &AgiEngine::cmd_reposition_to,
+ &AgiEngine::cmd_reposition_to_f,
+ &AgiEngine::cmd_trace_on,
+ &AgiEngine::cmd_trace_info,
+ &AgiEngine::cmd_print_at,
+ &AgiEngine::cmd_print_at_v, // 0x98
+ &AgiEngine::cmd_discard_view, // Opcode repeated from 0x20 ?
+ &AgiEngine::cmd_clear_text_rect,
+ &AgiEngine::cmd_set_upper_left,
+ &AgiEngine::cmd_set_menu,
+ &AgiEngine::cmd_set_menu_item,
+ &AgiEngine::cmd_submit_menu,
+ &AgiEngine::cmd_enable_item,
+ &AgiEngine::cmd_disable_item, // 0xa0
+ &AgiEngine::cmd_menu_input,
+ &AgiEngine::cmd_show_obj_v,
+ &AgiEngine::cmd_open_dialogue,
+ &AgiEngine::cmd_close_dialogue,
+ &AgiEngine::cmd_mul_n,
+ &AgiEngine::cmd_mul_v,
+ &AgiEngine::cmd_div_n,
+ &AgiEngine::cmd_div_v, // 0xa8
+ &AgiEngine::cmd_close_window,
+ &AgiEngine::cmd_set_simple,
+ &AgiEngine::cmd_push_script,
+ &AgiEngine::cmd_pop_script,
+ &AgiEngine::cmd_hold_key,
+ &AgiEngine::cmd_set_pri_base,
+ &AgiEngine::cmd_discard_sound,
+ &AgiEngine::cmd_hide_mouse, // 0xb0
+ &AgiEngine::cmd_allow_menu,
+ &AgiEngine::cmd_show_mouse,
+ &AgiEngine::cmd_fence_mouse,
+ &AgiEngine::cmd_mouse_posn,
+ &AgiEngine::cmd_release_key,
+ &AgiEngine::cmd_adj_ego_move_to_x_y
+ };
+ assert(ARRAYSIZE(_agiCommands) == ARRAYSIZE(tmp));
+ for (int i = 0; i < ARRAYSIZE(tmp); ++i)
+ _agiCommands[i] = tmp[i];
+}
/**
* Execute a logic script
@@ -1781,6 +1770,9 @@ int AgiEngine::runLogic(int n) {
int num = 0;
ScriptPos sp;
+ debugC(2, kDebugLevelScripts, "=================");
+ debugC(2, kDebugLevelScripts, "runLogic(%d)", n);
+
sp.script = n;
sp.curIP = 0;
_game.execStack.push_back(sp);
@@ -1792,12 +1784,12 @@ int AgiEngine::runLogic(int n) {
}
_game.lognum = n;
- curLogic = &_game.logics[_game.lognum];
+ _curLogic = &_game.logics[_game.lognum];
- code = curLogic->data;
- curLogic->cIP = curLogic->sIP;
+ code = _curLogic->data;
+ _curLogic->cIP = _curLogic->sIP;
- timerHack = 0;
+ _timerHack = 0;
while (ip < _game.logics[n].size && !(shouldQuit() || _restartGame)) {
if (_debug.enabled) {
if (_debug.steps > 0) {
@@ -1807,6 +1799,7 @@ int AgiEngine::runLogic(int n) {
}
} else {
_sprites->blitBoth();
+ _sprites->commitBoth();
do {
mainCycle();
} while (!_debug.steps && _debug.enabled);
@@ -1816,6 +1809,11 @@ int AgiEngine::runLogic(int n) {
_game.execStack.back().curIP = ip;
+ char st[101];
+ int sz = MIN(_game.execStack.size(), 100u);
+ memset(st, '.', sz);
+ st[sz] = 0;
+
switch (op = *(code + ip++)) {
case 0xff: // if (open/close)
testIfCode(n);
@@ -1826,13 +1824,16 @@ int AgiEngine::runLogic(int n) {
// timer must keep running even in goto loops,
// but AGI engine can't do that :(
- if (timerHack > 20) {
+ if (_timerHack > 20) {
pollTimer();
updateTimer();
- timerHack = 0;
+ _timerHack = 0;
}
break;
case 0x00: // return
+ debugC(2, kDebugLevelScripts, "%sreturn() // Logic %d", st, n);
+ debugC(2, kDebugLevelScripts, "=================");
+
_game.execStack.pop_back();
return 1;
default:
@@ -1840,8 +1841,9 @@ int AgiEngine::runLogic(int n) {
memmove(p, code + ip, num);
memset(p + num, 0, CMD_BSIZE - num);
- debugC(2, kDebugLevelScripts, "%s(%d %d %d)", logicNamesCmd[op].name, p[0], p[1], p[2]);
- agiCommand[op](this, p);
+ debugC(2, kDebugLevelScripts, "%s%s(%d %d %d)", st, logicNamesCmd[op].name, p[0], p[1], p[2]);
+
+ (this->*_agiCommands[op])(p);
ip += num;
}
@@ -1857,7 +1859,7 @@ int AgiEngine::runLogic(int n) {
void AgiEngine::executeAgiCommand(uint8 op, uint8 *p) {
debugC(2, kDebugLevelScripts, "%s(%d %d %d)", logicNamesCmd[op].name, p[0], p[1], p[2]);
- agiCommand[op] (this, p);
+ (this->*_agiCommands[op])(p);
}
} // End of namespace Agi
diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp
index 71d307556b..ab4f6cadc5 100644
--- a/engines/agi/op_test.cpp
+++ b/engines/agi/op_test.cpp
@@ -29,37 +29,23 @@
namespace Agi {
-static uint8 testObjRight(uint8, uint8, uint8, uint8, uint8);
-static uint8 testObjCentre(uint8, uint8, uint8, uint8, uint8);
-static uint8 testObjInBox(uint8, uint8, uint8, uint8, uint8);
-static uint8 testPosn(uint8, uint8, uint8, uint8, uint8);
-static uint8 testSaid(uint8, uint8 *);
-static uint8 testController(uint8);
-static uint8 testKeypressed();
-static uint8 testCompareStrings(uint8, uint8);
-
-static AgiEngine *g_agi;
-#define game g_agi->_game
-
-#define ip (game.logics[lognum].cIP)
-#define code (game.logics[lognum].data)
-
-#define testEqual(v1, v2) (g_agi->getvar(v1) == (v2))
-#define testLess(v1, v2) (g_agi->getvar(v1) < (v2))
-#define testGreater(v1, v2) (g_agi->getvar(v1) > (v2))
-#define testIsSet(flag) (g_agi->getflag(flag))
-#define testHas(obj) (g_agi->objectGetLocation(obj) == EGO_OWNED)
-#define testObjInRoom(obj, v) (g_agi->objectGetLocation(obj) == g_agi->getvar(v))
-
-extern int timerHack; // For the timer loop in MH1 logic 153
-
-static uint8 testCompareStrings(uint8 s1, uint8 s2) {
+#define ip (_game.logics[lognum].cIP)
+#define code (_game.logics[lognum].data)
+
+#define testEqual(v1, v2) (getvar(v1) == (v2))
+#define testLess(v1, v2) (getvar(v1) < (v2))
+#define testGreater(v1, v2) (getvar(v1) > (v2))
+#define testIsSet(flag) (getflag(flag))
+#define testHas(obj) (objectGetLocation(obj) == EGO_OWNED)
+#define testObjInRoom(obj, v) (objectGetLocation(obj) == getvar(v))
+
+uint8 AgiEngine::testCompareStrings(uint8 s1, uint8 s2) {
char ms1[MAX_STRINGLEN];
char ms2[MAX_STRINGLEN];
int j, k, l;
- strcpy(ms1, game.strings[s1]);
- strcpy(ms2, game.strings[s2]);
+ strcpy(ms1, _game.strings[s1]);
+ strcpy(ms2, _game.strings[s2]);
l = strlen(ms1);
for (k = 0, j = 0; k < l; k++) {
@@ -106,16 +92,16 @@ static uint8 testCompareStrings(uint8 s1, uint8 s2) {
return !strcmp(ms1, ms2);
}
-static uint8 testKeypressed() {
- int x = game.keypress;
+uint8 AgiEngine::testKeypressed() {
+ int x = _game.keypress;
- game.keypress = 0;
+ _game.keypress = 0;
if (!x) {
- int mode = game.inputMode;
+ InputMode mode = _game.inputMode;
- game.inputMode = INPUT_NONE;
- g_agi->mainCycle();
- game.inputMode = mode;
+ _game.inputMode = INPUT_NONE;
+ mainCycle();
+ _game.inputMode = mode;
}
if (x)
@@ -124,12 +110,12 @@ static uint8 testKeypressed() {
return x;
}
-static uint8 testController(uint8 cont) {
- return (game.controllerOccured[cont] ? 1 : 0);
+uint8 AgiEngine::testController(uint8 cont) {
+ return (_game.controllerOccured[cont] ? 1 : 0);
}
-static uint8 testPosn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testPosn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
uint8 r;
r = v->xPos >= x1 && v->yPos >= y1 && v->xPos <= x2 && v->yPos <= y2;
@@ -139,35 +125,35 @@ static uint8 testPosn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
return r;
}
-static uint8 testObjInBox(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testObjInBox(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
return v->xPos >= x1 &&
v->yPos >= y1 && v->xPos + v->xSize - 1 <= x2 && v->yPos <= y2;
}
// if n is in centre of box
-static uint8 testObjCentre(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testObjCentre(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
return v->xPos + v->xSize / 2 >= x1 &&
v->xPos + v->xSize / 2 <= x2 && v->yPos >= y1 && v->yPos <= y2;
}
// if nect N is in right corner
-static uint8 testObjRight(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testObjRight(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
return v->xPos + v->xSize - 1 >= x1 &&
v->xPos + v->xSize - 1 <= x2 && v->yPos >= y1 && v->yPos <= y2;
}
// When player has entered something, it is parsed elsewhere
-static uint8 testSaid(uint8 nwords, uint8 *cc) {
- int c, n = game.numEgoWords;
+uint8 AgiEngine::testSaid(uint8 nwords, uint8 *cc) {
+ int c, n = _game.numEgoWords;
int z = 0;
- if (g_agi->getflag(fSaidAcceptedInput) || !g_agi->getflag(fEnteredCli))
+ if (getflag(fSaidAcceptedInput) || !getflag(fEnteredCli))
return false;
// FR:
@@ -198,7 +184,7 @@ static uint8 testSaid(uint8 nwords, uint8 *cc) {
case 1: // any word
break;
default:
- if (game.egoWords[c].id != z)
+ if (_game.egoWords[c].id != z)
return false;
break;
}
@@ -213,13 +199,12 @@ static uint8 testSaid(uint8 nwords, uint8 *cc) {
if (nwords != 0 && READ_LE_UINT16(cc) != 9999)
return false;
- g_agi->setflag(fSaidAcceptedInput, true);
+ setflag(fSaidAcceptedInput, true);
return true;
}
int AgiEngine::testIfCode(int lognum) {
- g_agi = this;
int ec = true;
int retval = true;
uint8 op = 0;
@@ -263,32 +248,32 @@ int AgiEngine::testIfCode(int lognum) {
case 0x01:
ec = testEqual(p[0], p[1]);
if (p[0] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x02:
ec = testEqual(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x03:
ec = testLess(p[0], p[1]);
if (p[0] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x04:
ec = testLess(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x05:
ec = testGreater(p[0], p[1]);
if (p[0] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x06:
ec = testGreater(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x07:
ec = testIsSet(p[0]);
@@ -319,7 +304,7 @@ int AgiEngine::testIfCode(int lognum) {
ip++; // skip num_words opcode
break;
case 0x0F:
- debugC(7, kDebugLevelScripts, "comparing [%s], [%s]", game.strings[p[0]], game.strings[p[1]]);
+ debugC(7, kDebugLevelScripts, "comparing [%s], [%s]", _game.strings[p[0]], _game.strings[p[1]]);
ec = testCompareStrings(p[0], p[1]);
break;
case 0x10:
@@ -338,7 +323,7 @@ int AgiEngine::testIfCode(int lognum) {
// This command is used at least in the Amiga version of Gold Rush! v2.05 1989-03-09
// (AGI 2.316) in logics 1, 3, 5, 6, 137 and 192 (Logic.192 revealed this command's nature).
// TODO: Check this command's implementation using disassembly just to be sure.
- ec = game.viewTable[0].flags & ADJ_EGO_XY;
+ ec = _game.viewTable[0].flags & ADJ_EGO_XY;
debugC(7, kDebugLevelScripts, "op_test: in.motion.using.mouse = %s (Amiga-specific testcase 19)", ec ? "true" : "false");
break;
default:
diff --git a/engines/agi/picture.cpp b/engines/agi/picture.cpp
index 60877de430..dc77433cb2 100644
--- a/engines/agi/picture.cpp
+++ b/engines/agi/picture.cpp
@@ -476,7 +476,7 @@ void PictureMgr::plotPattern(int x, int y) {
// new purpose for temp16
- temp16 =( pen_size<<1) +1; // pen size
+ temp16 = (pen_size << 1) + 1; // pen size
pen_final_y += temp16; // the last row of this shape
temp16 = temp16 << 1;
pen_width = temp16; // width of shape?
@@ -495,7 +495,7 @@ void PictureMgr::plotPattern(int x, int y) {
} else {
circleCond = ((_patCode & 0x10) != 0);
counterStep = 4;
- ditherCond = 0x02;
+ ditherCond = 0x01;
}
for (; pen_y < pen_final_y; pen_y++) {
@@ -503,10 +503,12 @@ void PictureMgr::plotPattern(int x, int y) {
for (counter = 0; counter <= pen_width; counter += counterStep) {
if (circleCond || ((binary_list[counter>>1] & circle_word) != 0)) {
- temp8 = t % 2;
- t = t >> 1;
- if (temp8 != 0)
- t = t ^ 0xB8;
+ if ((_patCode & 0x20) != 0) {
+ temp8 = t % 2;
+ t = t >> 1;
+ if (temp8 != 0)
+ t = t ^ 0xB8;
+ }
// == box plot, != circle plot
if ((_patCode & 0x20) == 0 || (t & 0x03) == ditherCond)
diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp
index 35285798d4..1a5698dffc 100644
--- a/engines/agi/preagi.cpp
+++ b/engines/agi/preagi.cpp
@@ -68,8 +68,8 @@ void PreAgiEngine::initialize() {
// drivers, and I'm not sure what they are. For now, they might
// as well be called "PC Speaker" and "Not PC Speaker".
- switch (MidiDriver::detectMusicDriver(MDT_PCSPK)) {
- case MD_PCSPK:
+ switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK))) {
+ case MT_PCSPK:
_soundemu = SOUND_EMU_PC;
break;
default:
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index b7e830dc53..88b14dcfe2 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -132,7 +132,7 @@ int AgiEngine::saveGame(const char *fileName, const char *description) {
out->writeSint16BE((int16)_game.hasPrompt);
out->writeSint16BE((int16)_game.gameFlags);
- out->writeSint16BE((int16)_game.inputEnabled);
+ out->writeSint16BE(_game.inputEnabled);
for (i = 0; i < _HEIGHT; i++)
out->writeByte(_game.priTable[i]);
@@ -302,7 +302,7 @@ int AgiEngine::loadGame(const char *fileName, bool checkId) {
// TODO: played time
}
- _game.state = in->readByte();
+ _game.state = (State)in->readByte();
in->read(loadId, 8);
if (strcmp(loadId, _game.id) && checkId) {
@@ -361,7 +361,7 @@ int AgiEngine::loadGame(const char *fileName, bool checkId) {
_game.echoBuffer[0] = 0;
_game.keypress = 0;
- _game.inputMode = in->readSint16BE();
+ _game.inputMode = (InputMode)in->readSint16BE();
_game.lognum = in->readSint16BE();
_game.playerControl = in->readSint16BE();
@@ -864,6 +864,10 @@ int AgiEngine::saveGameDialog() {
sprintf(fileName, "%s", getSavegameFilename(_firstSlot + slot));
debugC(8, kDebugLevelMain | kDebugLevelResources, "file is [%s]", fileName);
+ // Make sure all graphics was blitted to screen. This fixes bug
+ // #2960567: "AGI: Ego partly erased in Load/Save thumbnails"
+ _gfx->doUpdate();
+
int result = saveGame(fileName, desc);
if (result == errOK)
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp
index ca5d42d981..cb4e307ea6 100644
--- a/engines/agi/sound.cpp
+++ b/engines/agi/sound.cpp
@@ -23,23 +23,21 @@
*
*/
-#include "common/md5.h"
-#include "common/config-manager.h"
-#include "common/fs.h"
-#include "common/random.h"
-#include "common/str-array.h"
-
#include "agi/agi.h"
-namespace Agi {
+#include "agi/sound_2gs.h"
+#include "agi/sound_coco3.h"
+#include "agi/sound_midi.h"
+#include "agi/sound_sarien.h"
+#include "agi/sound_pcjr.h"
-#define USE_INTERPOLATION
+namespace Agi {
//
// TODO: add support for variable sampling rate in the output device
//
-AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager) {
+AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager, int soundemu) {
if (data == NULL || len < 2) // Check for too small resource or no resource at all
return NULL;
uint16 type = READ_LE_UINT16(data);
@@ -48,27 +46,19 @@ AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, S
case AGI_SOUND_SAMPLE:
return new IIgsSample(data, len, resnum, manager);
case AGI_SOUND_MIDI:
- return new IIgsMidi (data, len, resnum, manager);
+ return new IIgsMidi(data, len, resnum, manager);
case AGI_SOUND_4CHN:
- return new PCjrSound (data, len, resnum, manager);
+ if (soundemu == SOUND_EMU_MIDI) {
+ return new MIDISound(data, len, resnum, manager);
+ } else {
+ return new PCjrSound(data, len, resnum, manager);
+ }
}
warning("Sound resource (%d) has unknown type (0x%04x). Not using the sound", resnum, type);
return NULL;
}
-IIgsMidi::IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
- _data = data; // Save the resource pointer
- _ptr = _data + 2; // Set current position to just after the header
- _len = len; // Save the resource's length
- _type = READ_LE_UINT16(data); // Read sound resource's type
- _midiTicks = _soundBufTicks = 0;
- _isValid = (_type == AGI_SOUND_MIDI) && (_data != NULL) && (_len >= 2);
-
- if (!_isValid) // Check for errors
- warning("Error creating Apple IIGS midi sound from resource %d (Type %d, length %d)", resnum, _type, len);
-}
-
PCjrSound::PCjrSound(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
_data = data; // Save the resource pointer
_len = len; // Save the resource's length
@@ -86,247 +76,12 @@ const uint8 *PCjrSound::getVoicePointer(uint voiceNum) {
return _data + voiceStartOffset;
}
-IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
- Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES);
-
- // Check that the header was read ok and that it's of the correct type
- if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource
- uint32 sampleStartPos = stream.pos();
- uint32 tailLen = stream.size() - sampleStartPos;
-
- if (tailLen < _header.sampleSize) { // Check if there's no room for the sample data in the stream
- // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes
- // of sample data although header says it should have 16384 bytes.
- warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left",
- resnum, tailLen, _header.sampleSize);
-
- _header.sampleSize = (uint16) tailLen; // Use the part that's left
- }
-
- if (_header.pitch > 0x7F) { // Check if the pitch is invalid
- warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, _header.pitch);
-
- _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too
- }
-
- // Finalize the header info using the 8-bit unsigned sample data
- _header.finalize(stream);
-
- // Convert sample data from 8-bit unsigned to 8-bit signed format
- stream.seek(sampleStartPos);
- _sample = new int8[_header.sampleSize];
-
- if (_sample != NULL)
- _isValid = SoundMgr::convertWave(stream, _sample, _header.sampleSize);
- }
-
- if (!_isValid) // Check for errors
- warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len);
-}
-
-/** Reads an Apple IIGS envelope from then given stream. */
-bool IIgsEnvelope::read(Common::SeekableReadStream &stream) {
- for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) {
- seg[segNum].bp = stream.readByte();
- seg[segNum].inc = stream.readUint16LE();
- }
-
- return !(stream.eos() || stream.err());
-}
-
-/** Reads an Apple IIGS wave information structure from the given stream. */
-bool IIgsWaveInfo::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
- top = stream.readByte();
- addr = stream.readByte() * 256;
- size = (1 << (stream.readByte() & 7)) * 256;
-
- // Read packed mode byte and parse it into parts
- byte packedModeByte = stream.readByte();
- channel = (packedModeByte >> 4) & 1; // Bit 4
- mode = (packedModeByte >> 1) & 3; // Bits 1-2
- halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean)
-
- relPitch = stream.readSint16LE();
-
- // Zero the wave address if we want to ignore the wave address info
- if (ignoreAddr)
- addr = 0;
-
- return !(stream.eos() || stream.err());
-}
-
-bool IIgsWaveInfo::finalize(Common::SeekableReadStream &uint8Wave) {
- uint32 startPos = uint8Wave.pos(); // Save stream's starting position
- uint8Wave.seek(addr, SEEK_CUR); // Seek to wave's address
-
- // Calculate the true sample size (A zero ends the sample prematurely)
- uint trueSize = size; // Set a default value for the result
- for (uint i = 0; i < size; i++) {
- if (uint8Wave.readByte() == 0) {
- trueSize = i;
- // A zero in the sample stream turns off looping
- // (At least that's what MESS 0.117 and KEGS32 0.91 seem to do)
- if (mode == OSC_MODE_LOOP)
- mode = OSC_MODE_ONESHOT;
- break;
- }
- }
- size = trueSize; // Set the true sample size
-
- uint8Wave.seek(startPos); // Seek back to the stream's starting position
-
- return true;
-}
-
-bool IIgsOscillator::finalize(Common::SeekableReadStream &uint8Wave) {
- for (uint i = 0; i < WAVES_PER_OSCILLATOR; i++)
- if (!waves[i].finalize(uint8Wave))
- return false;
-
- return true;
-}
-
-bool IIgsOscillatorList::read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr) {
- // First read the A waves and then the B waves for the oscillators
- for (uint waveNum = 0; waveNum < WAVES_PER_OSCILLATOR; waveNum++)
- for (uint oscNum = 0; oscNum < oscillatorCount; oscNum++)
- if (!osc[oscNum].waves[waveNum].read(stream, ignoreAddr))
- return false;
-
- count = oscillatorCount; // Set the oscillator count
-
- return true;
-}
-
-bool IIgsOscillatorList::finalize(Common::SeekableReadStream &uint8Wave) {
- for (uint i = 0; i < count; i++)
- if (!osc[i].finalize(uint8Wave))
- return false;
-
- return true;
-}
-
-bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
- env.read(stream);
- relseg = stream.readByte();
- /*byte priority =*/ stream.readByte(); // Not needed? 32 in all tested data.
- bendrange = stream.readByte();
- vibdepth = stream.readByte();
- vibspeed = stream.readByte();
- /*byte spare =*/ stream.readByte(); // Not needed? 0 in all tested data.
- byte wac = stream.readByte(); // Read A wave count
- byte wbc = stream.readByte(); // Read B wave count
- oscList.read(stream, wac, ignoreAddr); // Read the oscillators
- return (wac == wbc) && !(stream.eos() || stream.err()); // A and B wave counts must match
-}
-
-bool IIgsInstrumentHeader::finalize(Common::SeekableReadStream &uint8Wave) {
- return oscList.finalize(uint8Wave);
-}
-
-bool IIgsSampleHeader::read(Common::SeekableReadStream &stream) {
- type = stream.readUint16LE();
- pitch = stream.readByte();
- unknownByte_Ofs3 = stream.readByte();
- volume = stream.readByte();
- unknownByte_Ofs5 = stream.readByte();
- instrumentSize = stream.readUint16LE();
- sampleSize = stream.readUint16LE();
- // Read the instrument header *ignoring* its wave address info
-
- return instrument.read(stream, true);
-}
-
-bool IIgsSampleHeader::finalize(Common::SeekableReadStream &uint8Wave) {
- return instrument.finalize(uint8Wave);
-}
-
-/** Older Apple IIGS AGI MIDI program change to instrument number mapping. */
-static const MidiProgramChangeMapping progToInstMappingV1 = {
- {19, 20, 22, 23, 21, 24, 5, 5, 5, 5,
- 6, 7, 10, 9, 11, 9, 15, 8, 5, 5,
- 17, 16, 18, 12, 14, 5, 5, 5, 5, 5,
- 0, 1, 2, 9, 3, 4, 15, 2, 2, 2,
- 25, 13, 13, 25},
- 5
-};
-
-/** Newer Apple IIGS AGI MIDI program change to instrument number mapping. */
-static const MidiProgramChangeMapping progToInstMappingV2 = {
- {21, 22, 24, 25, 23, 26, 6, 6, 6, 6,
- 7, 9, 12, 8, 13, 11, 17, 10, 6, 6,
- 19, 18, 20, 14, 16, 6, 6, 6, 6, 6,
- 0, 1, 2, 4, 3, 5, 17, 2, 2, 2,
- 27, 15, 15, 27},
- 6
-};
-
-/** Older Apple IIGS AGI instrument set. Used only by Space Quest I (AGI v1.002). */
-static const InstrumentSetInfo instSetV1 = {
- 1192, 26, "7ee16bbc135171ffd6b9120cc7ff1af2", "edd3bf8905d9c238e02832b732fb2e18", progToInstMappingV1
-};
-
-/** Newer Apple IIGS AGI instrument set (AGI v1.003+). Used by all others than Space Quest I. */
-static const InstrumentSetInfo instSetV2 = {
- 1292, 28, "b7d428955bb90721996de1cbca25e768", "c05fb0b0e11deefab58bc68fbd2a3d07", progToInstMappingV2
-};
-
-/** Information about different Apple IIGS AGI executables. */
-static const IIgsExeInfo IIgsExeInfos[] = {
- {GID_SQ1, "SQ", 0x1002, 138496, 0x80AD, instSetV1},
- {GID_LSL1, "LL", 0x1003, 141003, 0x844E, instSetV2},
- {GID_AGIDEMO, "DEMO", 0x1005, 141884, 0x8469, instSetV2},
- {GID_KQ1, "KQ", 0x1006, 141894, 0x8469, instSetV2},
- {GID_PQ1, "PQ", 0x1007, 141882, 0x8469, instSetV2},
- {GID_MIXEDUP, "MG", 0x1013, 142552, 0x84B7, instSetV2},
- {GID_KQ2, "KQ2", 0x1013, 143775, 0x84B7, instSetV2},
- {GID_KQ3, "KQ3", 0x1014, 144312, 0x84B7, instSetV2},
- {GID_SQ2, "SQ2", 0x1014, 107882, 0x6563, instSetV2},
- {GID_MH1, "MH", 0x2004, 147678, 0x8979, instSetV2},
- {GID_KQ4, "KQ4", 0x2006, 147652, 0x8979, instSetV2},
- {GID_BC, "BC", 0x3001, 148192, 0x8979, instSetV2},
- {GID_GOLDRUSH, "GR", 0x3003, 148268, 0x8979, instSetV2}
-};
-
-static const int16 waveformRamp[WAVEFORM_SIZE] = {
- 0, 8, 16, 24, 32, 40, 48, 56,
- 64, 72, 80, 88, 96, 104, 112, 120,
- 128, 136, 144, 152, 160, 168, 176, 184,
- 192, 200, 208, 216, 224, 232, 240, 255,
- 0, -248, -240, -232, -224, -216, -208, -200,
- -192, -184, -176, -168, -160, -152, -144, -136,
- -128, -120, -112, -104, -96, -88, -80, -72,
- -64, -56, -48, -40, -32, -24, -16, -8 // Ramp up
-};
-
-static const int16 waveformSquare[WAVEFORM_SIZE] = {
- 255, 230, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 110,
- -255, -230, -220, -220, -220, -220, -220, -220,
- -220, -220, -220, -220, -220, -220, -220, -220,
- -220, -220, -220, -220, -220, -220, -220, -220,
- -220, -220, -220, -110, 0, 0, 0, 0 // Square
-};
-
-static const int16 waveformMac[WAVEFORM_SIZE] = {
- 45, 110, 135, 161, 167, 173, 175, 176,
- 156, 137, 123, 110, 91, 72, 35, -2,
- -60, -118, -142, -165, -170, -176, -177, -179,
- -177, -176, -164, -152, -117, -82, -17, 47,
- 92, 137, 151, 166, 170, 173, 171, 169,
- 151, 133, 116, 100, 72, 43, -7, -57,
- -99, -141, -156, -170, -174, -177, -178, -179,
- -175, -172, -165, -159, -137, -114, -67, -19
-};
-
+#if 0
static const uint16 period[] = {
1024, 1085, 1149, 1218, 1290, 1367,
1448, 1534, 1625, 1722, 1825, 1933
};
-#if 0
static int noteToPeriod(int note) {
return 10 * (period[note % 12] >> (note / 12 - 3));
}
@@ -346,8 +101,7 @@ void SoundMgr::unloadSound(int resnum) {
}
void SoundMgr::startSound(int resnum, int flag) {
- int i;
- AgiSoundType type;
+ AgiSoundEmuType type;
if (_vm->_game.sounds[resnum] != NULL && _vm->_game.sounds[resnum]->isPlaying())
return;
@@ -357,7 +111,7 @@ void SoundMgr::startSound(int resnum, int flag) {
if (_vm->_game.sounds[resnum] == NULL) // Is this needed at all?
return;
- type = (AgiSoundType)_vm->_game.sounds[resnum]->type();
+ type = (AgiSoundEmuType)_vm->_game.sounds[resnum]->type();
if (type != AGI_SOUND_SAMPLE && type != AGI_SOUND_MIDI && type != AGI_SOUND_4CHN)
return;
@@ -367,39 +121,8 @@ void SoundMgr::startSound(int resnum, int flag) {
debugC(3, kDebugLevelSound, "startSound(resnum = %d, flag = %d) type = %d", resnum, flag, type);
- switch (type) {
- case AGI_SOUND_SAMPLE: {
- IIgsSample *sampleRes = (IIgsSample *) _vm->_game.sounds[_playingSound];
- _gsSound.playSampleSound(sampleRes->getHeader(), sampleRes->getSample());
- break;
- }
- case AGI_SOUND_MIDI:
- ((IIgsMidi *) _vm->_game.sounds[_playingSound])->rewind();
- break;
- case AGI_SOUND_4CHN:
- PCjrSound *pcjrSound = (PCjrSound *) _vm->_game.sounds[resnum];
-
- // Initialize channel info
- for (i = 0; i < NUM_CHANNELS; i++) {
- _chn[i].type = type;
- _chn[i].flags = AGI_SOUND_LOOP;
-
- if (_env) {
- _chn[i].flags |= AGI_SOUND_ENVELOPE;
- _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
- }
-
- _chn[i].ins = _waveform;
- _chn[i].size = WAVEFORM_SIZE;
- _chn[i].ptr = pcjrSound->getVoicePointer(i % 4);
- _chn[i].timer = 0;
- _chn[i].vol = 0;
- _chn[i].end = 0;
- }
- break;
- }
+ _soundGen->play(resnum);
- memset(_sndBuffer, 0, BUFFER_SIZE << 1);
_endflag = flag;
// Nat Budin reports that the flag should be reset when sound starts
@@ -407,907 +130,68 @@ void SoundMgr::startSound(int resnum, int flag) {
}
void SoundMgr::stopSound() {
- int i;
-
debugC(3, kDebugLevelSound, "stopSound() --> %d", _playingSound);
_endflag = -1;
- if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
- for (i = 0; i < NUM_CHANNELS; i++)
- stopNote(i);
- }
if (_playingSound != -1) {
if (_vm->_game.sounds[_playingSound]) // sanity checking
_vm->_game.sounds[_playingSound]->stop();
- if (_vm->_soundemu == SOUND_EMU_APPLE2GS) {
- _gsSound.stopSounds();
- }
+ _soundGen->stop();
_playingSound = -1;
}
-}
-void IIgsSoundMgr::stopSounds() {
- // Stops all sounds on all MIDI channels
- for (iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
- iter->stopSounds();
+ if (_endflag != -1)
+ _vm->setflag(_endflag, true);
}
-bool IIgsSoundMgr::playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample) {
- stopSounds();
- IIgsMidiChannel &channel = _midiChannels[kSfxMidiChannel];
+int SoundMgr::initSound() {
+ return -1;
+}
- channel.setInstrument(&sampleHeader.instrument, sample);
- channel.setVolume(sampleHeader.volume);
- channel.noteOn(sampleHeader.pitch, 64); // Use default velocity (i.e. 64)
+void SoundMgr::deinitSound() {
+ stopSound();
- return true;
+ delete _soundGen;
}
-void IIgsMidiChannel::stopSounds() {
- // Stops all sounds on this single MIDI channel
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- iter->stop();
+void SoundMgr::soundIsFinished() {
+ if (_endflag != -1)
+ _vm->setflag(_endflag, true);
- _gsChannels.clear();
+ if (_playingSound != -1)
+ _vm->_game.sounds[_playingSound]->stop();
+ _playingSound = -1;
+ _endflag = -1;
}
-int SoundMgr::initSound() {
- int r = -1;
-
- memset(_sndBuffer, 0, BUFFER_SIZE << 1);
- _env = false;
+SoundMgr::SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer) {
+ _vm = agi;
+ _endflag = -1;
+ _playingSound = -1;
switch (_vm->_soundemu) {
case SOUND_EMU_NONE:
- _waveform = waveformRamp;
- _env = true;
- break;
case SOUND_EMU_AMIGA:
- case SOUND_EMU_PC:
- _waveform = waveformSquare;
- break;
case SOUND_EMU_MAC:
- _waveform = waveformMac;
+ _soundGen = new SoundGenSarien(_vm, pMixer);
+ break;
+ case SOUND_EMU_PC:
+ case SOUND_EMU_PCJR:
+ _soundGen = new SoundGenPCJr(_vm, pMixer);
break;
case SOUND_EMU_APPLE2GS:
- _disabledMidi = !loadInstruments();
+ _soundGen = new SoundGen2GS(_vm, pMixer);
break;
case SOUND_EMU_COCO3:
+ _soundGen = new SoundGenCoCo3(_vm, pMixer);
break;
- }
-
- report("Initializing sound:\n");
-
- report("sound: envelopes ");
- if (_env) {
- report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
- } else {
- report("disabled\n");
- }
-
- _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
- return r;
-}
-
-void SoundMgr::deinitSound() {
- debugC(3, kDebugLevelSound, "()");
-
- _mixer->stopHandle(_soundHandle);
-}
-
-void SoundMgr::stopNote(int i) {
- _chn[i].adsr = AGI_SOUND_ENV_RELEASE;
-
- if (_useChorus) {
- // Stop chorus ;)
- if (_chn[i].type == AGI_SOUND_4CHN &&
- _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
- stopNote(i + 4);
- }
- }
-}
-
-void SoundMgr::playNote(int i, int freq, int vol) {
- if (!_vm->getflag(fSoundOn))
- vol = 0;
- else if (vol && _vm->_soundemu == SOUND_EMU_PC)
- vol = 160;
-
- _chn[i].phase = 0;
- _chn[i].freq = freq;
- _chn[i].vol = vol;
- _chn[i].env = 0x10000;
- _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
-
- if (_useChorus) {
- // Add chorus ;)
- if (_chn[i].type == AGI_SOUND_4CHN &&
- _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
-
- int newfreq = freq * 1007 / 1000;
-
- if (freq == newfreq)
- newfreq++;
-
- playNote(i + 4, newfreq, vol * 2 / 3);
- }
- }
-}
-
-void SoundMgr::playMidiSound() {
- if (_disabledMidi)
- return;
-
- const uint8 *p;
- uint8 parm1, parm2;
- static uint8 cmd, ch;
-
- if (_playingSound == -1 || _vm->_game.sounds[_playingSound] == NULL) {
- warning("Error playing Apple IIGS MIDI sound resource");
- _playing = false;
-
- return;
- }
-
- IIgsMidi *midiObj = (IIgsMidi *) _vm->_game.sounds[_playingSound];
-
- _playing = true;
- p = midiObj->getPtr();
-
- midiObj->_soundBufTicks++;
-
- while (true) {
- uint8 readByte = *p;
-
- // Check for end of MIDI sequence marker (Can also be here before delta-time)
- if (readByte == MIDI_BYTE_STOP_SEQUENCE) {
- debugC(3, kDebugLevelSound, "End of MIDI sequence (Before reading delta-time)");
- _playing = false;
-
- midiObj->rewind();
-
- return;
- } else if (readByte == MIDI_BYTE_TIMER_SYNC) {
- debugC(3, kDebugLevelSound, "Timer sync");
- p++; // Jump over the timer sync byte as it's not needed
-
- continue;
- }
-
- uint8 deltaTime = readByte;
- if (midiObj->_midiTicks + deltaTime > midiObj->_soundBufTicks) {
- break;
- }
- midiObj->_midiTicks += deltaTime;
- p++; // Jump over the delta-time byte as it was already taken care of
-
- // Check for end of MIDI sequence marker (This time it after reading delta-time)
- if (*p == MIDI_BYTE_STOP_SEQUENCE) {
- debugC(3, kDebugLevelSound, "End of MIDI sequence (After reading delta-time)");
- _playing = false;
-
- midiObj->rewind();
-
- return;
- }
-
- // Separate byte into command and channel if it's a command byte.
- // Otherwise use running status (i.e. previously set command and channel).
- if (*p & 0x80) {
- cmd = *p++;
- ch = cmd & 0x0f;
- cmd >>= 4;
- }
-
- switch (cmd) {
- case MIDI_CMD_NOTE_OFF:
- parm1 = *p++;
- parm2 = *p++;
- _gsSound.midiNoteOff(ch, parm1, parm2);
- break;
- case MIDI_CMD_NOTE_ON:
- parm1 = *p++;
- parm2 = *p++;
- _gsSound.midiNoteOn(ch, parm1, parm2);
- break;
- case MIDI_CMD_CONTROLLER:
- parm1 = *p++;
- parm2 = *p++;
- _gsSound.midiController(ch, parm1, parm2);
- break;
- case MIDI_CMD_PROGRAM_CHANGE:
- parm1 = *p++;
- _gsSound.midiProgramChange(ch, parm1);
- break;
- case MIDI_CMD_PITCH_WHEEL:
- parm1 = *p++;
- parm2 = *p++;
-
- uint16 wheelPos = ((parm2 & 0x7F) << 7) | (parm1 & 0x7F); // 14-bit value
- _gsSound.midiPitchWheel(wheelPos);
- break;
- }
- }
-
- midiObj->setPtr(p);
-}
-
-void IIgsSoundMgr::midiNoteOff(uint8 channel, uint8 note, uint8 velocity) {
- _midiChannels[channel].noteOff(note, velocity);
- debugC(3, kDebugLevelSound, "note off, channel %02x, note %02x, velocity %02x", channel, note, velocity);
-}
-
-void IIgsSoundMgr::midiNoteOn(uint8 channel, uint8 note, uint8 velocity) {
- _midiChannels[channel].noteOn(note, velocity);
- debugC(3, kDebugLevelSound, "note on, channel %02x, note %02x, velocity %02x", channel, note, velocity);
-}
-
-// TODO: Check if controllers behave differently on different MIDI channels
-// TODO: Doublecheck what other controllers than the volume controller do
-void IIgsSoundMgr::midiController(uint8 channel, uint8 controller, uint8 value) {
- IIgsMidiChannel &midiChannel = _midiChannels[channel];
-
- // The tested Apple IIGS AGI MIDI resources only used
- // controllers 0 (Bank select?), 7 (Volume) and 64 (Sustain On/Off).
- // Controller 0's parameter was in range 94-127,
- // controller 7's parameter was in range 0-127 and
- // controller 64's parameter was always 0 (i.e. sustain off).
- bool unimplemented = false;
- switch (controller) {
- case 7: // Volume
- midiChannel.setVolume(value);
- break;
- default:
- unimplemented = true;
+ case SOUND_EMU_MIDI:
+ _soundGen = new SoundGenMIDI(_vm, pMixer);
break;
}
- debugC(3, kDebugLevelSound, "controller %02x, ch %02x, val %02x%s", controller, channel, value, unimplemented ? " (Unimplemented)" : "");
-}
-
-void IIgsSoundMgr::midiProgramChange(uint8 channel, uint8 program) {
- _midiChannels[channel].setInstrument(getInstrument(program), _wave.begin());
- debugC(3, kDebugLevelSound, "program change %02x, channel %02x", program, channel);
-}
-
-void IIgsSoundMgr::midiPitchWheel(uint8 wheelPos) {
- // In all the tested Apple IIGS AGI MIDI resources
- // pitch wheel commands always used 0x2000 (Center position).
- // Therefore it should be quite safe to ignore this command.
- debugC(3, kDebugLevelSound, "pitch wheel position %04x (Unimplemented)", wheelPos);
-}
-
-IIgsSoundMgr::IIgsSoundMgr() {
- _midiChannels.resize(16); // Set the amount of available MIDI channels
-}
-
-const IIgsInstrumentHeader* IIgsSoundMgr::getInstrument(uint8 program) const {
- return &_instruments[_midiProgToInst->map(program)];
-}
-
-void IIgsSoundMgr::setProgramChangeMapping(const MidiProgramChangeMapping *mapping) {
- _midiProgToInst = mapping;
-}
-
-void IIgsSoundMgr::removeStoppedSounds() {
- for (Common::Array<IIgsMidiChannel>::iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
- iter->removeStoppedSounds();
-}
-
-void IIgsMidiChannel::removeStoppedSounds() {
- for (int i = _gsChannels.size() - 1; i >= 0; i--)
- if (!_gsChannels[i].playing())
- _gsChannels.remove_at(i);
-}
-
-uint IIgsSoundMgr::activeSounds() const {
- uint result = 0;
-
- for (Common::Array<IIgsMidiChannel>::const_iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
- result += iter->activeSounds();
-
- return result;
-}
-
-uint IIgsMidiChannel::activeSounds() const {
- uint result = 0;
-
- for (const_iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- if (!iter->end)
- result++;
-
- return result;
-}
-
-void IIgsMidiChannel::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
- _instrument = instrument;
- _sample = sample;
-
- // Set program on each Apple IIGS channel playing on this MIDI channel
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- iter->setInstrument(instrument, sample);
-}
-
-void IIgsMidiChannel::setVolume(uint8 volume) {
- _volume = volume;
-
- // Set volume on each Apple IIGS channel playing on this MIDI channel
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- iter->setChannelVolume(volume);
-}
-
-void IIgsMidiChannel::noteOff(uint8 note, uint8 velocity) {
- // Go through all the notes playing on this MIDI channel
- // and turn off the ones that are playing the given note
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- if (iter->origNote == note)
- iter->noteOff(velocity);
-}
-
-void IIgsMidiChannel::noteOn(uint8 note, uint8 velocity) {
- IIgsChannelInfo channel;
-
- // Use the default channel volume and instrument
- channel.setChannelVolume(_volume);
- channel.setInstrument(_instrument, _sample);
-
- // Set the note on and save the channel
- channel.noteOn(note, velocity);
- _gsChannels.push_back(channel);
-}
-
-void IIgsChannelInfo::rewind() {
- this->envVol = this->startEnvVol;
- this->envSeg = 0;
- this->pos = intToFrac(0);
-}
-
-void IIgsChannelInfo::setChannelVolume(uint8 volume) {
- this->chanVol = intToFrac(volume);
-}
-
-void IIgsChannelInfo::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
- assert(instrument != NULL && sample != NULL);
- this->ins = instrument;
- this->unrelocatedSample = sample;
-}
-
-// TODO/FIXME: Implement correctly and fully (Take velocity into account etc)
-void IIgsChannelInfo::noteOn(uint8 noteParam, uint8 velocity) {
- this->origNote = noteParam;
- this->startEnvVol = intToFrac(0);
- rewind();
-
- const IIgsWaveInfo *waveInfo = NULL;
-
- for (uint i = 0; i < ins->oscList.count; i++)
- if (ins->oscList(i).waves[0].top >= noteParam)
- waveInfo = &ins->oscList(i).waves[0];
-
- assert(waveInfo != NULL);
-
- this->relocatedSample = this->unrelocatedSample + waveInfo->addr;
- this->posAdd = intToFrac(0);
- this->note = intToFrac(noteParam) + doubleToFrac(waveInfo->relPitch/256.0);
- this->vol = doubleToFrac(fracToDouble(this->envVol) * fracToDouble(this->chanVol) / 127.0);
- this->loop = (waveInfo->mode == OSC_MODE_LOOP);
- this->size = waveInfo->size - waveInfo->addr;
- this->end = waveInfo->halt;
-}
-
-// TODO/FIXME: Implement correctly and fully (Take release time and velocity into account etc)
-void IIgsChannelInfo::noteOff(uint8 velocity) {
- this->loop = false;
- this->envSeg = ins->relseg;
-}
-
-void IIgsChannelInfo::stop() {
- this->end = true;
-}
-
-bool IIgsChannelInfo::playing() {
- return !this->end;
-}
-
-void SoundMgr::playSampleSound() {
- if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
- warning("Trying to play a sample but not using Apple IIGS sound emulation mode");
- return;
- }
-
- if (_playingSound != -1)
- _playing = _gsSound.activeSounds() > 0;
-}
-
-static int cocoFrequencies[] = {
- 130, 138, 146, 155, 164, 174, 184, 195, 207, 220, 233, 246,
- 261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493,
- 523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987,
- 1046, 1108, 1174, 1244, 1318, 1396, 1479, 1567, 1661, 1760, 1864, 1975,
- 2093, 2217, 2349, 2489, 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951
-};
-
-void SoundMgr::playCoCoSound() {
- int i = 0;
- CoCoNote note;
-
- do {
- note.read(_chn[i].ptr);
-
- if (note.freq != 0xff) {
- playNote(0, cocoFrequencies[note.freq], note.volume);
-
- uint32 start_time = _vm->_system->getMillis();
-
- while (_vm->_system->getMillis() < start_time + note.duration) {
- _vm->_system->updateScreen();
-
- _vm->_system->delayMillis(10);
- }
- }
- } while (note.freq != 0xff);
-}
-
-void SoundMgr::playAgiSound() {
- int i;
- AgiNote note;
-
- _playing = false;
- for (i = 0; i < (_vm->_soundemu == SOUND_EMU_PC ? 1 : 4); i++) {
- _playing |= !_chn[i].end;
- note.read(_chn[i].ptr); // Read a single note (Doesn't advance the pointer)
-
- if (_chn[i].end)
- continue;
-
- if ((--_chn[i].timer) <= 0) {
- stopNote(i);
-
- if (note.freqDiv != 0) {
- int volume = (note.attenuation == 0x0F) ? 0 : (0xFF - note.attenuation * 2);
- playNote(i, note.freqDiv * 10, volume);
- }
-
- _chn[i].timer = note.duration;
-
- if (_chn[i].timer == 0xffff) {
- _chn[i].end = 1;
- _chn[i].vol = 0;
- _chn[i].env = 0;
-
- if (_useChorus) {
- // chorus
- if (_chn[i].type == AGI_SOUND_4CHN && _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
- _chn[i + 4].vol = 0;
- _chn[i + 4].env = 0;
- }
- }
- }
- _chn[i].ptr += 5; // Advance the pointer to the next note data (5 bytes per note)
- }
- }
-}
-
-void SoundMgr::playSound() {
- int i;
-
- if (_endflag == -1)
- return;
-
- if (_vm->_soundemu == SOUND_EMU_APPLE2GS) {
- if (_playingSound != -1) {
- if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_MIDI) {
- playMidiSound();
- //warning("playSound: Trying to play an Apple IIGS MIDI sound. Not yet implemented");
- } else if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_SAMPLE) {
- //debugC(3, kDebugLevelSound, "playSound: Trying to play an Apple IIGS sample");
- playSampleSound();
- }
- }
- } else if (_vm->_soundemu == SOUND_EMU_COCO3) {
- playCoCoSound();
- } else {
- //debugC(3, kDebugLevelSound, "playSound: Trying to play a PCjr 4-channel sound");
- playAgiSound();
- }
-
- if (!_playing) {
- if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
- for (i = 0; i < NUM_CHANNELS; _chn[i++].vol = 0)
- ;
- }
-
- if (_endflag != -1)
- _vm->setflag(_endflag, true);
-
- if (_playingSound != -1)
- _vm->_game.sounds[_playingSound]->stop();
- _playingSound = -1;
- _endflag = -1;
- }
-}
-
-uint32 SoundMgr::mixSound() {
- register int i, p;
- const int16 *src;
- int c, b, m;
-
- memset(_sndBuffer, 0, BUFFER_SIZE << 1);
-
- if (!_playing || _playingSound == -1)
- return BUFFER_SIZE;
-
- // Handle Apple IIGS sound mixing here
- // TODO: Implement playing both waves in an oscillator
- // TODO: Implement swap-mode in an oscillator
- if (_vm->_soundemu == SOUND_EMU_APPLE2GS) {
- for (uint midiChan = 0; midiChan < _gsSound._midiChannels.size(); midiChan++) {
- for (uint gsChan = 0; gsChan < _gsSound._midiChannels[midiChan]._gsChannels.size(); gsChan++) {
- IIgsChannelInfo &channel = _gsSound._midiChannels[midiChan]._gsChannels[gsChan];
- if (channel.playing()) { // Only mix in actively playing channels
- // Frequency multiplier was 1076.0 based on tests made with MESS 0.117.
- // Tests made with KEGS32 averaged the multiplier to around 1045.
- // So this is a guess but maybe it's 1046.5... i.e. C6's frequency?
- double hertz = C6_FREQ * pow(SEMITONE, fracToDouble(channel.note));
- channel.posAdd = doubleToFrac(hertz / getRate());
- channel.vol = doubleToFrac(fracToDouble(channel.envVol) * fracToDouble(channel.chanVol) / 127.0);
- double tempVol = fracToDouble(channel.vol)/127.0;
- for (i = 0; i < IIGS_BUFFER_SIZE; i++) {
- b = channel.relocatedSample[fracToInt(channel.pos)];
- // TODO: Find out what volume/amplification setting is loud enough
- // but still doesn't clip when playing many channels on it.
- _sndBuffer[i] += (int16) (b * tempVol * 256/4);
- channel.pos += channel.posAdd;
-
- if (channel.pos >= intToFrac(channel.size)) {
- if (channel.loop) {
- // Don't divide by zero on zero length samples
- channel.pos %= intToFrac(channel.size + (channel.size == 0));
- // Probably we should loop the envelope too
- channel.envSeg = 0;
- channel.envVol = channel.startEnvVol;
- } else {
- channel.pos = channel.chanVol = 0;
- channel.end = true;
- break;
- }
- }
- }
-
- if (channel.envSeg < ENVELOPE_SEGMENT_COUNT) {
- const IIgsEnvelopeSegment &seg = channel.ins->env.seg[channel.envSeg];
- // I currently assume enveloping works with the same speed as the MIDI
- // (i.e. with 1/60ths of a second ticks).
- // TODO: Check if enveloping really works with the same speed as MIDI
- frac_t envVolDelta = doubleToFrac(seg.inc/256.0);
- if (intToFrac(seg.bp) >= channel.envVol) {
- channel.envVol += envVolDelta;
- if (channel.envVol >= intToFrac(seg.bp)) {
- channel.envVol = intToFrac(seg.bp);
- channel.envSeg += 1;
- }
- } else {
- channel.envVol -= envVolDelta;
- if (channel.envVol <= intToFrac(seg.bp)) {
- channel.envVol = intToFrac(seg.bp);
- channel.envSeg += 1;
- }
- }
- }
- }
- }
- }
- _gsSound.removeStoppedSounds();
- return IIGS_BUFFER_SIZE;
- } // else ...
-
- // Handle PCjr 4-channel sound mixing here
- for (c = 0; c < NUM_CHANNELS; c++) {
- if (!_chn[c].vol)
- continue;
-
- m = _chn[c].flags & AGI_SOUND_ENVELOPE ?
- _chn[c].vol * _chn[c].env >> 16 : _chn[c].vol;
-
- if (_chn[c].type != AGI_SOUND_4CHN || c != 3) {
- src = _chn[c].ins;
-
- p = _chn[c].phase;
- for (i = 0; i < BUFFER_SIZE; i++) {
- b = src[p >> 8];
-#ifdef USE_INTERPOLATION
- b += ((src[((p >> 8) + 1) % _chn[c].size] - src[p >> 8]) * (p & 0xff)) >> 8;
-#endif
- _sndBuffer[i] += (b * m) >> 4;
-
- p += (uint32) 118600 *4 / _chn[c].freq;
-
- // FIXME: Fingolfin asks: why is there a FIXME here? Please either clarify what
- // needs fixing, or remove it!
- // FIXME
- if (_chn[c].flags & AGI_SOUND_LOOP) {
- p %= _chn[c].size << 8;
- } else {
- if (p >= _chn[c].size << 8) {
- p = _chn[c].vol = 0;
- _chn[c].end = 1;
- break;
- }
- }
-
- }
- _chn[c].phase = p;
- } else {
- // Add white noise
- for (i = 0; i < BUFFER_SIZE; i++) {
- b = _vm->_rnd->getRandomNumber(255) - 128;
- _sndBuffer[i] += (b * m) >> 4;
- }
- }
-
- switch (_chn[c].adsr) {
- case AGI_SOUND_ENV_ATTACK:
- // not implemented
- _chn[c].adsr = AGI_SOUND_ENV_DECAY;
- break;
- case AGI_SOUND_ENV_DECAY:
- if (_chn[c].env > _chn[c].vol * ENV_SUSTAIN + ENV_DECAY) {
- _chn[c].env -= ENV_DECAY;
- } else {
- _chn[c].env = _chn[c].vol * ENV_SUSTAIN;
- _chn[c].adsr = AGI_SOUND_ENV_SUSTAIN;
- }
- break;
- case AGI_SOUND_ENV_SUSTAIN:
- break;
- case AGI_SOUND_ENV_RELEASE:
- if (_chn[c].env >= ENV_RELEASE) {
- _chn[c].env -= ENV_RELEASE;
- } else {
- _chn[c].env = 0;
- }
- }
- }
-
- return BUFFER_SIZE;
-}
-
-/**
- * Finds information about an Apple IIGS AGI executable based on the game ID.
- * @return A non-null IIgsExeInfo pointer if successful, otherwise NULL.
- */
-const IIgsExeInfo *SoundMgr::getIIgsExeInfo(enum AgiGameID gameid) const {
- for (int i = 0; i < ARRAYSIZE(IIgsExeInfos); i++)
- if (IIgsExeInfos[i].gameid == gameid)
- return &IIgsExeInfos[i];
- return NULL;
-}
-
-bool IIgsSoundMgr::loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo) {
- bool loadedOk = false; // Was loading successful?
- Common::File file;
-
- // Open the executable file and check that it has correct size
- file.open(exePath);
- if (file.size() != (int32)exeInfo.exeSize) {
- debugC(3, kDebugLevelSound, "Apple IIGS executable (%s) has wrong size (Is %d, should be %d)",
- exePath.getPath().c_str(), file.size(), exeInfo.exeSize);
- }
-
- // Read the whole executable file into memory
- Common::SharedPtr<Common::MemoryReadStream> data(file.readStream(file.size()));
- file.close();
-
- // Check that we got enough data to be able to parse the instruments
- if (data && data->size() >= (int32)(exeInfo.instSetStart + exeInfo.instSet.byteCount)) {
- // Check instrument set's length (The info's saved in the executable)
- data->seek(exeInfo.instSetStart - 4);
- uint16 instSetByteCount = data->readUint16LE();
- if (instSetByteCount != exeInfo.instSet.byteCount) {
- debugC(3, kDebugLevelSound, "Wrong instrument set size (Is %d, should be %d) in Apple IIGS executable (%s)",
- instSetByteCount, exeInfo.instSet.byteCount, exePath.getPath().c_str());
- }
-
- // Check instrument set's md5sum
- data->seek(exeInfo.instSetStart);
-
- char md5str[32+1];
- Common::md5_file_string(*data, md5str, exeInfo.instSet.byteCount);
- if (scumm_stricmp(md5str, exeInfo.instSet.md5)) {
- warning("Unknown Apple IIGS instrument set (md5: %s) in %s, trying to use it nonetheless",
- md5str, exePath.getPath().c_str());
- }
-
- // Read in the instrument set one instrument at a time
- data->seek(exeInfo.instSetStart);
-
- // Load the instruments
- _instruments.clear();
- _instruments.reserve(exeInfo.instSet.instCount);
-
- IIgsInstrumentHeader instrument;
- for (uint i = 0; i < exeInfo.instSet.instCount; i++) {
- if (!instrument.read(*data)) {
- warning("Error loading Apple IIGS instrument (%d. of %d) from %s, not loading more instruments",
- i + 1, exeInfo.instSet.instCount, exePath.getPath().c_str());
- break;
- }
- _instruments.push_back(instrument); // Add the successfully loaded instrument to the instruments array
- }
-
- // Loading was successful only if all instruments were loaded successfully
- loadedOk = (_instruments.size() == exeInfo.instSet.instCount);
- } else // Couldn't read enough data from the executable file
- warning("Error loading instruments from Apple IIGS executable (%s)", exePath.getPath().c_str());
-
- return loadedOk;
-}
-
-/**
- * Convert sample from 8-bit unsigned to 8-bit signed format.
- * @param source Source stream containing the 8-bit unsigned sample data.
- * @param dest Destination buffer for the 8-bit signed sample data.
- * @param length Length of the sample data to be converted.
- */
-bool SoundMgr::convertWave(Common::SeekableReadStream &source, int8 *dest, uint length) {
- // Convert the wave from 8-bit unsigned to 8-bit signed format
- for (uint i = 0; i < length; i++)
- dest[i] = (int8) ((int) source.readByte() - 128);
- return !(source.eos() || source.err());
-}
-
-bool IIgsSoundMgr::loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo) {
- Common::File file;
-
- // Open the wave file and read it into memory
- file.open(wavePath);
- Common::SharedPtr<Common::MemoryReadStream> uint8Wave(file.readStream(file.size()));
- file.close();
-
- // Check that we got the whole wave file
- if (uint8Wave && uint8Wave->size() == SIERRASTANDARD_SIZE) {
- // Check wave file's md5sum
- char md5str[32+1];
- Common::md5_file_string(*uint8Wave, md5str, SIERRASTANDARD_SIZE);
- if (scumm_stricmp(md5str, exeInfo.instSet.waveFileMd5)) {
- warning("Unknown Apple IIGS wave file (md5: %s, game: %s).\n" \
- "Please report the information on the previous line to the ScummVM team.\n" \
- "Using the wave file as it is - music may sound weird", md5str, exeInfo.exePrefix);
- }
-
- uint8Wave->seek(0); // Seek wave to its start
- // Convert the wave file from 8-bit unsigned to 8-bit signed and save the result
- _wave.resize(uint8Wave->size());
- return SoundMgr::convertWave(*uint8Wave, _wave.begin(), uint8Wave->size());
- } else { // Couldn't read the wave file or it had incorrect size
- warning("Error loading Apple IIGS wave file (%s), not loading instruments", wavePath.getPath().c_str());
- return false;
- }
-}
-
-/**
- * A function object (i.e. a functor) for testing if a Common::FSNode
- * object's name is equal (Ignoring case) to a string or to at least
- * one of the strings in a list of strings. Can be used e.g. with find_if().
- */
-struct fsnodeNameEqualsIgnoreCase : public Common::UnaryFunction<const Common::FSNode&, bool> {
-// FIXME: This should be replaced; use SearchMan instead
- fsnodeNameEqualsIgnoreCase(const Common::StringArray &str) : _str(str) {}
- fsnodeNameEqualsIgnoreCase(const Common::String str) { _str.push_back(str); }
- bool operator()(const Common::FSNode &param) const {
- for (Common::StringArray::const_iterator iter = _str.begin(); iter != _str.end(); ++iter)
- if (param.getName().equalsIgnoreCase(*iter))
- return true;
- return false;
- }
-private:
- Common::StringArray _str;
-};
-
-bool SoundMgr::loadInstruments() {
- // Check that the platform is Apple IIGS, as only it uses custom instruments
- if (_vm->getPlatform() != Common::kPlatformApple2GS) {
- debugC(3, kDebugLevelSound, "Platform isn't Apple IIGS so not loading any instruments");
- return true;
- }
-
- // Get info on the particular Apple IIGS AGI game's executable
- const IIgsExeInfo *exeInfo = getIIgsExeInfo((enum AgiGameID) _vm->getGameID());
- if (exeInfo == NULL) {
- warning("Unsupported Apple IIGS game, not loading instruments");
- return false;
- }
-
- // List files in the game path
- Common::FSList fslist;
- Common::FSNode dir(ConfMan.get("path"));
- if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) {
- warning("Invalid game path (\"%s\"), not loading Apple IIGS instruments", dir.getPath().c_str());
- return false;
- }
-
- // Populate executable filenames list (Long filename and short filename) for searching
- Common::StringArray exeNames;
- exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS16");
- exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS");
-
- // Populate wave filenames list (Long filename and short filename) for searching
- Common::StringArray waveNames;
- waveNames.push_back("SIERRASTANDARD");
- waveNames.push_back("SIERRAST");
-
- // Search for the executable file and the wave file (i.e. check if any of the filenames match)
- Common::FSList::const_iterator exeFsnode, waveFsnode;
- exeFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(exeNames));
- waveFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(waveNames));
-
- // Make sure that we found the executable file
- if (exeFsnode == fslist.end()) {
- warning("Couldn't find Apple IIGS game executable (%s), not loading instruments", exeNames.begin()->c_str());
- return false;
- }
-
- // Make sure that we found the wave file
- if (waveFsnode == fslist.end()) {
- warning("Couldn't find Apple IIGS wave file (%s), not loading instruments", waveNames.begin()->c_str());
- return false;
- }
-
- // Set the MIDI program change to instrument number mapping and
- // load the instrument headers and their sample data.
- // None of the tested SIERRASTANDARD-files have zeroes in them so
- // there's no need to check for prematurely ending samples here.
- _gsSound.setProgramChangeMapping(&exeInfo->instSet.progToInst);
- return _gsSound.loadWaveFile(*waveFsnode, *exeInfo) && _gsSound.loadInstrumentHeaders(*exeFsnode, *exeInfo);
-}
-
-void SoundMgr::fillAudio(void *udata, int16 *stream, uint len) {
- SoundMgr *soundMgr = (SoundMgr *)udata;
- uint32 p = 0;
-
- // current number of audio bytes in _sndBuffer
- static uint32 data_available = 0;
- // offset of start of audio bytes in _sndBuffer
- static uint32 data_offset = 0;
-
- len <<= 2;
-
- debugC(5, kDebugLevelSound, "(%p, %p, %d)", (void *)udata, (void *)stream, len);
-
- while (len > data_available) {
- memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, data_available);
- p += data_available;
- len -= data_available;
-
- soundMgr->playSound();
- data_available = soundMgr->mixSound() << 1;
- data_offset = 0;
- }
-
- memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, len);
- data_offset += len;
- data_available -= len;
-}
-
-SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) : _chn() {
- _vm = agi;
- _mixer = pMixer;
- _sampleRate = pMixer->getOutputRate();
- _endflag = -1;
- _playingSound = -1;
- _env = false;
- _playing = false;
- _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
- _waveform = 0;
- _disabledMidi = false;
- _useChorus = true; // FIXME: Currently always true?
-}
-
-void SoundMgr::premixerCall(int16 *data, uint len) {
- fillAudio(this, data, len);
}
void SoundMgr::setVolume(uint8 volume) {
@@ -1315,7 +199,6 @@ void SoundMgr::setVolume(uint8 volume) {
}
SoundMgr::~SoundMgr() {
- free(_sndBuffer);
}
} // End of namespace Agi
diff --git a/engines/agi/sound.h b/engines/agi/sound.h
index 881e3efd56..63b36e017c 100644
--- a/engines/agi/sound.h
+++ b/engines/agi/sound.h
@@ -26,160 +26,18 @@
#ifndef AGI_SOUND_H
#define AGI_SOUND_H
-#include "sound/audiostream.h"
#include "sound/mixer.h"
-#include "common/frac.h"
namespace Agi {
-#define BUFFER_SIZE 410
-
-// Apple IIGS MIDI uses 60 ticks per second (Based on tests with Apple IIGS
-// KQ1 and SQ1 under MESS 0.124a). So we make the audio buffer size to be a
-// 1/60th of a second in length. That should be getSampleRate() / 60 samples
-// in length but as getSampleRate() is always 22050 at the moment we just use
-// the hardcoded value of 368 (22050/60 = 367.5 which rounds up to 368).
-// FIXME: Use getSampleRate() / 60 rather than a hardcoded value
-#define IIGS_BUFFER_SIZE 368
-
#define SOUND_EMU_NONE 0
#define SOUND_EMU_PC 1
-#define SOUND_EMU_TANDY 2
+#define SOUND_EMU_PCJR 2
#define SOUND_EMU_MAC 3
#define SOUND_EMU_AMIGA 4
#define SOUND_EMU_APPLE2GS 5
#define SOUND_EMU_COCO3 6
-
-#define WAVEFORM_SIZE 64
-#define ENV_ATTACK 10000 /**< envelope attack rate */
-#define ENV_DECAY 1000 /**< envelope decay rate */
-#define ENV_SUSTAIN 100 /**< envelope sustain level */
-#define ENV_RELEASE 7500 /**< envelope release rate */
-#define NUM_CHANNELS 7 /**< number of sound channels */
-
-// MIDI command values (Shifted right by 4 so they're in the lower nibble)
-#define MIDI_CMD_NOTE_OFF 0x08
-#define MIDI_CMD_NOTE_ON 0x09
-#define MIDI_CMD_CONTROLLER 0x0B
-#define MIDI_CMD_PROGRAM_CHANGE 0x0C
-#define MIDI_CMD_PITCH_WHEEL 0x0E
-// Whole MIDI byte values (Command and channel info together)
-#define MIDI_BYTE_STOP_SEQUENCE 0xFC
-#define MIDI_BYTE_TIMER_SYNC 0xF8
-
-struct IIgsEnvelopeSegment {
- uint8 bp;
- uint16 inc; ///< 8b.8b fixed point, very probably little endian
-};
-
-#define ENVELOPE_SEGMENT_COUNT 8
-struct IIgsEnvelope {
- IIgsEnvelopeSegment seg[ENVELOPE_SEGMENT_COUNT];
-
- /** Reads an Apple IIGS envelope from then given stream. */
- bool read(Common::SeekableReadStream &stream);
-};
-
-// 2**(1/12) i.e. the 12th root of 2
-#define SEMITONE 1.059463094359295
-
-// C6's frequency is A4's (440 Hz) frequency but one full octave and three semitones higher
-// i.e. C6_FREQ = 440 * pow(2.0, 15/12.0)
-#define C6_FREQ 1046.502261202395
-
-// Size of the SIERRASTANDARD file (i.e. the wave file i.e. the sample data used by the instruments).
-#define SIERRASTANDARD_SIZE 65536
-
-// Maximum number of instruments in an Apple IIGS instrument set.
-// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
-#define MAX_INSTRUMENTS 28
-
-struct IIgsWaveInfo {
- uint8 top;
- uint addr;
- uint size;
-// Oscillator channel
-#define OSC_CHANNEL_RIGHT 0
-#define OSC_CHANNEL_LEFT 1
- uint channel;
-// Oscillator mode
-#define OSC_MODE_LOOP 0
-#define OSC_MODE_ONESHOT 1
-#define OSC_MODE_SYNC_AM 2
-#define OSC_MODE_SWAP 3
- uint mode;
- bool halt;
- int16 relPitch; ///< Relative pitch in semitones (Signed 8b.8b fixed point)
-
- /** Reads an Apple IIGS wave information structure from the given stream. */
- bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-// Number of waves per Apple IIGS sound oscillator
-#define WAVES_PER_OSCILLATOR 2
-
-/** An Apple IIGS sound oscillator. Consists always of two waves. */
-struct IIgsOscillator {
- IIgsWaveInfo waves[WAVES_PER_OSCILLATOR];
-
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-// Maximum number of oscillators in an Apple IIGS instrument.
-// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
-#define MAX_OSCILLATORS 4
-
-/** An Apple IIGS sound oscillator list. */
-struct IIgsOscillatorList {
- uint count; ///< Oscillator count
- IIgsOscillator osc[MAX_OSCILLATORS]; ///< The oscillators
-
- /** Indexing operators for easier access to the oscillators. */
- const IIgsOscillator &operator()(uint index) const { return osc[index]; }
- IIgsOscillator &operator()(uint index) { return osc[index]; }
-
- /** Reads an Apple IIGS oscillator list from the given stream. */
- bool read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr = false);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-struct IIgsInstrumentHeader {
- IIgsEnvelope env;
- uint8 relseg;
- uint8 bendrange;
- uint8 vibdepth;
- uint8 vibspeed;
- IIgsOscillatorList oscList;
-
- /**
- * Read an Apple IIGS instrument header from the given stream.
- * @param stream The source stream from which to read the data.
- * @param ignoreAddr Should we ignore wave infos' wave address variable's value?
- * @return True if successful, false otherwise.
- */
- bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-struct IIgsSampleHeader {
- uint16 type;
- uint8 pitch; ///< Logarithmic, base is 2**(1/12), unknown multiplier (Possibly in range 1040-1080)
- uint8 unknownByte_Ofs3; // 0x7F in Gold Rush's sound resource 60, 0 in all others.
- uint8 volume; ///< Current guess: Logarithmic in 6 dB steps
- uint8 unknownByte_Ofs5; ///< 0 in all tested samples.
- uint16 instrumentSize; ///< Little endian. 44 in all tested samples. A guess.
- uint16 sampleSize; ///< Little endian. Accurate in all tested samples excluding Manhunter I's sound resource 16.
- IIgsInstrumentHeader instrument;
-
- /**
- * Read an Apple IIGS AGI sample header from the given stream.
- * @param stream The source stream from which to read the data.
- * @return True if successful, false otherwise.
- */
- bool read(Common::SeekableReadStream &stream);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
+#define SOUND_EMU_MIDI 7
/**
* AGI sound note structure.
@@ -200,87 +58,38 @@ struct AgiNote {
}
};
-struct IIgsChannelInfo {
- const IIgsInstrumentHeader *ins; ///< Instrument info
- const int8 *relocatedSample; ///< Source sample data (8-bit signed format) using relocation
- const int8 *unrelocatedSample; ///< Source sample data (8-bit signed format) without relocation
- frac_t pos; ///< Current sample position
- frac_t posAdd; ///< Current sample position adder (Calculated using note, vibrato etc)
- uint8 origNote; ///< The original note without the added relative pitch
- frac_t note; ///< Note (With the added relative pitch)
- frac_t vol; ///< Current volume (Takes both channel volume and enveloping into account)
- frac_t chanVol; ///< Channel volume
- frac_t startEnvVol; ///< Starting envelope volume
- frac_t envVol; ///< Current envelope volume
- uint envSeg; ///< Current envelope segment
- uint size; ///< Sample size
- bool loop; ///< Should we loop the sample?
- bool end; ///< Has the playing ended?
-
- void rewind(); ///< Rewinds the sound playing on this channel to its start
- void setChannelVolume(uint8 volume); ///< Sets the channel volume
- void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample); ///< Sets the instrument to be used on this channel
- void noteOn(uint8 noteParam, uint8 velocity); ///< Starts playing a note on this channel
- void noteOff(uint8 velocity); ///< Releases the note on this channel
- void stop(); ///< Stops the note playing on this channel instantly
- bool playing(); ///< Is there a note playing on this channel?
-};
-
-struct CoCoNote {
- uint8 freq;
- uint8 volume;
- uint16 duration; ///< Note duration
-
- /** Reads a CoCoNote through the given pointer. */
- void read(const uint8 *ptr) {
- freq = *ptr;
- volume = *(ptr + 1);
- duration = READ_LE_UINT16(ptr + 2);
- }
-};
-
/**
* AGI sound resource types.
* It's probably coincidence that all the values here are powers of two
* as they're simply the different used values in AGI sound resources'
* starts (The first 16-bit little endian word, to be precise).
*/
-enum AgiSoundType {
+enum AgiSoundEmuType {
AGI_SOUND_SAMPLE = 0x0001,
AGI_SOUND_MIDI = 0x0002,
AGI_SOUND_4CHN = 0x0008
};
-enum AgiSoundFlags {
- AGI_SOUND_LOOP = 0x0001,
- AGI_SOUND_ENVELOPE = 0x0002
-};
-enum AgiSoundEnv {
- AGI_SOUND_ENV_ATTACK = 3,
- AGI_SOUND_ENV_DECAY = 2,
- AGI_SOUND_ENV_SUSTAIN = 1,
- AGI_SOUND_ENV_RELEASE = 0
-};
+class SoundMgr;
-/**
- * AGI engine sound channel structure.
- */
-struct ChannelInfo {
- AgiSoundType type;
- const uint8 *ptr; // Pointer to the AgiNote data
- const int16 *ins;
- int32 size;
- uint32 phase;
- uint32 flags; // ORs values from AgiSoundFlags
- AgiSoundEnv adsr;
- int32 timer;
- uint32 end;
- uint32 freq;
- uint32 vol;
- uint32 env;
-};
+class SoundGen {
+public:
+ SoundGen(AgiEngine *vm, Audio::Mixer *pMixer) : _vm(vm), _mixer(pMixer) {
+ _sampleRate = pMixer->getOutputRate();
+ }
-class SoundMgr;
+ virtual ~SoundGen() {}
+
+ virtual void play(int resnum) = 0;
+ virtual void stop(void) = 0;
+
+ AgiEngine *_vm;
+
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _soundHandle;
+
+ uint32 _sampleRate;
+};
/**
* AGI sound resource structure.
@@ -302,7 +111,7 @@ public:
* from memory using free() or delegate the responsibility onwards to some other
* function!
*/
- static AgiSound *createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ static AgiSound *createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager, int soundemu);
protected:
SoundMgr &_manager; ///< AGI sound manager object
@@ -313,7 +122,7 @@ protected:
class PCjrSound : public AgiSound {
public:
PCjrSound(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
- ~PCjrSound() { if (_data != NULL) free(_data); }
+ ~PCjrSound() { free(_data); }
virtual uint16 type() { return _type; }
const uint8 *getVoicePointer(uint voiceNum);
protected:
@@ -322,192 +131,30 @@ protected:
uint16 _type; ///< Sound resource type
};
-class IIgsMidi : public AgiSound {
-public:
- IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
- ~IIgsMidi() { if (_data != NULL) free(_data); }
- virtual uint16 type() { return _type; }
- virtual const uint8 *getPtr() { return _ptr; }
- virtual void setPtr(const uint8 *ptr) { _ptr = ptr; }
- virtual void rewind() { _ptr = _data + 2; _midiTicks = _soundBufTicks = 0; }
-protected:
- uint8 *_data; ///< Raw sound resource data
- const uint8 *_ptr; ///< Pointer to the current position in the MIDI data
- uint32 _len; ///< Length of the raw sound resource
- uint16 _type; ///< Sound resource type
-public:
- uint _midiTicks; ///< MIDI song position in ticks (1/60ths of a second)
- uint _soundBufTicks; ///< Sound buffer position in ticks (1/60ths of a second)
-};
-
-class IIgsSample : public AgiSound {
-public:
- IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
- ~IIgsSample() { delete[] _sample; }
- virtual uint16 type() { return _header.type; }
- const IIgsSampleHeader &getHeader() const { return _header; }
- const int8 *getSample() const { return _sample; }
-protected:
- IIgsSampleHeader _header; ///< Apple IIGS AGI sample header
- int8 *_sample; ///< Sample data (8-bit signed format)
-};
-
-/** Apple IIGS MIDI program change to instrument number mapping. */
-struct MidiProgramChangeMapping {
- byte midiProgToInst[44]; ///< Lookup table for the MIDI program number to instrument number mapping
- byte undefinedInst; ///< The undefined instrument number
-
- // Maps the MIDI program number to an instrument number
- byte map(uint midiProg) const {
- return midiProg < ARRAYSIZE(midiProgToInst) ? midiProgToInst[midiProg] : undefinedInst;
- }
-};
-
-/** Apple IIGS AGI instrument set information. */
-struct InstrumentSetInfo {
- uint byteCount; ///< Length of the whole instrument set in bytes
- uint instCount; ///< Amount of instrument in the set
- const char *md5; ///< MD5 hex digest of the whole instrument set
- const char *waveFileMd5; ///< MD5 hex digest of the wave file (i.e. the sample data used by the instruments)
- const MidiProgramChangeMapping &progToInst; ///< Program change to instrument number mapping
-};
-
-/** Apple IIGS AGI executable file information. */
-struct IIgsExeInfo {
- enum AgiGameID gameid; ///< Game ID
- const char *exePrefix; ///< Prefix of the Apple IIGS AGI executable (e.g. "SQ", "PQ", "KQ4" etc)
- uint agiVer; ///< Apple IIGS AGI version number, not strictly needed
- uint exeSize; ///< Size of the Apple IIGS AGI executable file in bytes
- uint instSetStart; ///< Starting offset of the instrument set inside the executable file
- const InstrumentSetInfo &instSet; ///< Information about the used instrument set
-};
-
-class IIgsMidiChannel {
-public:
- IIgsMidiChannel() : _instrument(0), _sample(0), _volume(0) {}
- uint activeSounds() const; ///< How many active sounds are playing?
- void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample);
- void setVolume(uint8 volume);
- void noteOff(uint8 note, uint8 velocity);
- void noteOn(uint8 note, uint8 velocity);
- void stopSounds(); ///< Clears the channel of any sounds
- void removeStoppedSounds(); ///< Removes all stopped sounds from this MIDI channel
-public:
- typedef Common::Array<IIgsChannelInfo>::const_iterator const_iterator;
- typedef Common::Array<IIgsChannelInfo>::iterator iterator;
- Common::Array<IIgsChannelInfo> _gsChannels; ///< Apple IIGS channels playing on this MIDI channel
-protected:
- const IIgsInstrumentHeader *_instrument; ///< Instrument used on this MIDI channel
- const int8 *_sample; ///< Sample data used on this MIDI channel
- uint8 _volume; ///< MIDI controller number 7 (Volume)
-};
-
-/**
- * Class for managing Apple IIGS sound channels.
- * TODO: Check what instruments are used by default on the MIDI channels
- * FIXME: Some instrument choices sound wrong
- */
-class IIgsSoundMgr {
-public:
- typedef Common::Array<IIgsMidiChannel>::const_iterator const_iterator;
- typedef Common::Array<IIgsMidiChannel>::iterator iterator;
- static const uint kSfxMidiChannel = 0; ///< The MIDI channel used for playing sound effects
-public:
- // For initializing
- IIgsSoundMgr();
- void setProgramChangeMapping(const MidiProgramChangeMapping *mapping);
- bool loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo);
- bool loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo);
- // Miscellaneous methods
- uint activeSounds() const; ///< How many active sounds are playing?
- void stopSounds(); ///< Stops all sounds
- void removeStoppedSounds(); ///< Removes all stopped sounds from the MIDI channels
- // For playing Apple IIGS AGI samples (Sound effects etc)
- bool playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample);
- // MIDI commands
- void midiNoteOff(uint8 channel, uint8 note, uint8 velocity);
- void midiNoteOn(uint8 channel, uint8 note, uint8 velocity);
- void midiController(uint8 channel, uint8 controller, uint8 value);
- void midiProgramChange(uint8 channel, uint8 program);
- void midiPitchWheel(uint8 wheelPos);
-protected:
- const IIgsInstrumentHeader* getInstrument(uint8 program) const;
-public:
- Common::Array<IIgsMidiChannel> _midiChannels; ///< Information about each MIDI channel
-protected:
- Common::Array<int8> _wave; ///< Sample data used by the Apple IIGS MIDI instruments
- const MidiProgramChangeMapping *_midiProgToInst; ///< MIDI program change to instrument number mapping
- Common::Array<IIgsInstrumentHeader> _instruments; ///< Instruments used by the Apple IIGS AGI
-};
-
-class AgiEngine;
-class AgiBase;
-
-class SoundMgr : public Audio::AudioStream {
- AgiBase *_vm;
+class SoundMgr {
public:
- SoundMgr(AgiBase *agi, Audio::Mixer *pMixer);
+ SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer);
~SoundMgr();
- virtual void setVolume(uint8 volume);
-
- // AudioStream API
- int readBuffer(int16 *buffer, const int numSamples) {
- premixerCall(buffer, numSamples / 2);
- return numSamples;
- }
-
- bool isStereo() const {
- return false;
- }
- bool endOfData() const {
- return false;
- }
-
- int getRate() const {
- // FIXME: Ideally, we should use _sampleRate.
- return 22050;
- }
-
-private:
- Audio::Mixer *_mixer;
- Audio::SoundHandle _soundHandle;
- uint32 _sampleRate;
-
- bool _playing;
- ChannelInfo _chn[NUM_CHANNELS];
- IIgsSoundMgr _gsSound;
- int _endflag;
- int _playingSound;
- uint8 _env;
- bool _disabledMidi;
-
- int16 *_sndBuffer;
- const int16 *_waveform;
-
- bool _useChorus;
-
- void premixerCall(int16 *buf, uint len);
- void fillAudio(void *udata, int16 *stream, uint len);
+ void setVolume(uint8 volume);
-public:
void unloadSound(int);
void playSound();
int initSound();
void deinitSound();
void startSound(int, int);
void stopSound();
- void stopNote(int i);
- void playNote(int i, int freq, int vol);
- void playAgiSound();
- void playCoCoSound();
- uint32 mixSound();
- bool loadInstruments();
- void playMidiSound();
- void playSampleSound();
- const IIgsExeInfo *getIIgsExeInfo(enum AgiGameID gameid) const;
- static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint length);
+
+ void soundIsFinished();
+
+private:
+ int _endflag;
+ AgiEngine *_vm;
+
+ SoundGen *_soundGen;
+
+ int _playingSound;
};
} // End of namespace Agi
diff --git a/engines/agi/sound_2gs.cpp b/engines/agi/sound_2gs.cpp
new file mode 100644
index 0000000000..cc1cd0f6d5
--- /dev/null
+++ b/engines/agi/sound_2gs.cpp
@@ -0,0 +1,919 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/config-manager.h"
+#include "common/fs.h"
+#include "common/md5.h"
+#include "common/str-array.h"
+
+#include "agi/agi.h"
+#include "agi/sound_2gs.h"
+
+namespace Agi {
+
+SoundGen2GS::SoundGen2GS(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) {
+ _disabledMidi = !loadInstruments();
+
+ _playingSound = -1;
+ _playing = false;
+
+ _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
+
+ _midiChannels.resize(16); // Set the amount of available MIDI channels
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundGen2GS::~SoundGen2GS() {
+ _mixer->stopHandle(_soundHandle);
+
+ free(_sndBuffer);
+}
+
+int SoundGen2GS::readBuffer(int16 *buffer, const int numSamples) {
+ fillAudio(buffer, numSamples / 2);
+
+ return numSamples;
+}
+
+void SoundGen2GS::play(int resnum) {
+ AgiSoundEmuType type;
+
+ _playingSound = resnum;
+
+ type = (AgiSoundEmuType)_vm->_game.sounds[resnum]->type();
+
+ assert (type == AGI_SOUND_SAMPLE || type == AGI_SOUND_MIDI);
+
+ switch (type) {
+ case AGI_SOUND_SAMPLE: {
+ IIgsSample *sampleRes = (IIgsSample *) _vm->_game.sounds[_playingSound];
+ playSampleSound(sampleRes->getHeader(), sampleRes->getSample());
+ break;
+ }
+ case AGI_SOUND_MIDI:
+ ((IIgsMidi *) _vm->_game.sounds[_playingSound])->rewind();
+ break;
+ default:
+ break;
+ }
+}
+
+void SoundGen2GS::stop() {
+ _playingSound = -1;
+
+ // Stops all sounds on all MIDI channels
+ for (iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ iter->stopSounds();
+}
+
+void SoundGen2GS::playSound() {
+ if (_playingSound == -1)
+ return;
+
+ if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_MIDI) {
+ playMidiSound();
+ //warning("playSound: Trying to play an Apple IIGS MIDI sound. Not yet implemented");
+ } else if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_SAMPLE) {
+ //debugC(3, kDebugLevelSound, "playSound: Trying to play an Apple IIGS sample");
+ playSampleSound();
+ }
+
+ if (!_playing) {
+ _vm->_sound->soundIsFinished();
+
+ _playingSound = -1;
+ }
+}
+
+uint32 SoundGen2GS::mixSound() {
+ int i, b;
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+
+ if (!_playing || _playingSound == -1)
+ return BUFFER_SIZE;
+
+ // Handle Apple IIGS sound mixing here
+ // TODO: Implement playing both waves in an oscillator
+ // TODO: Implement swap-mode in an oscillator
+ for (uint midiChan = 0; midiChan < _midiChannels.size(); midiChan++) {
+ for (uint gsChan = 0; gsChan < _midiChannels[midiChan]._gsChannels.size(); gsChan++) {
+ IIgsChannelInfo &channel = _midiChannels[midiChan]._gsChannels[gsChan];
+ if (channel.playing()) { // Only mix in actively playing channels
+ // Frequency multiplier was 1076.0 based on tests made with MESS 0.117.
+ // Tests made with KEGS32 averaged the multiplier to around 1045.
+ // So this is a guess but maybe it's 1046.5... i.e. C6's frequency?
+ double hertz = C6_FREQ * pow(SEMITONE, fracToDouble(channel.note));
+ channel.posAdd = doubleToFrac(hertz / getRate());
+ channel.vol = doubleToFrac(fracToDouble(channel.envVol) * fracToDouble(channel.chanVol) / 127.0);
+ double tempVol = fracToDouble(channel.vol)/127.0;
+ for (i = 0; i < IIGS_BUFFER_SIZE; i++) {
+ b = channel.relocatedSample[fracToInt(channel.pos)];
+ // TODO: Find out what volume/amplification setting is loud enough
+ // but still doesn't clip when playing many channels on it.
+ _sndBuffer[i] += (int16) (b * tempVol * 256/4);
+ channel.pos += channel.posAdd;
+
+ if (channel.pos >= intToFrac(channel.size)) {
+ if (channel.loop) {
+ // Don't divide by zero on zero length samples
+ channel.pos %= intToFrac(channel.size + (channel.size == 0));
+ // Probably we should loop the envelope too
+ channel.envSeg = 0;
+ channel.envVol = channel.startEnvVol;
+ } else {
+ channel.pos = channel.chanVol = 0;
+ channel.end = true;
+ break;
+ }
+ }
+ }
+
+ if (channel.envSeg < ENVELOPE_SEGMENT_COUNT) {
+ const IIgsEnvelopeSegment &seg = channel.ins->env.seg[channel.envSeg];
+ // I currently assume enveloping works with the same speed as the MIDI
+ // (i.e. with 1/60ths of a second ticks).
+ // TODO: Check if enveloping really works with the same speed as MIDI
+ frac_t envVolDelta = doubleToFrac(seg.inc/256.0);
+ if (intToFrac(seg.bp) >= channel.envVol) {
+ channel.envVol += envVolDelta;
+ if (channel.envVol >= intToFrac(seg.bp)) {
+ channel.envVol = intToFrac(seg.bp);
+ channel.envSeg += 1;
+ }
+ } else {
+ channel.envVol -= envVolDelta;
+ if (channel.envVol <= intToFrac(seg.bp)) {
+ channel.envVol = intToFrac(seg.bp);
+ channel.envSeg += 1;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ removeStoppedSounds();
+
+ return IIGS_BUFFER_SIZE;
+}
+
+void SoundGen2GS::fillAudio(int16 *stream, uint len) {
+ uint32 p = 0;
+
+ // current number of audio bytes in _sndBuffer
+ static uint32 data_available = 0;
+ // offset of start of audio bytes in _sndBuffer
+ static uint32 data_offset = 0;
+
+ len <<= 2;
+
+ debugC(5, kDebugLevelSound, "(%p, %d)", (void *)stream, len);
+
+ while (len > data_available) {
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, data_available);
+ p += data_available;
+ len -= data_available;
+
+ playSound();
+ data_available = mixSound() << 1;
+ data_offset = 0;
+ }
+
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, len);
+ data_offset += len;
+ data_available -= len;
+}
+
+void SoundGen2GS::playSampleSound() {
+ if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
+ warning("Trying to play a sample but not using Apple IIGS sound emulation mode");
+ return;
+ }
+
+ if (_playingSound != -1)
+ _playing = activeSounds() > 0;
+}
+
+void SoundGen2GS::stopSounds() {
+ // Stops all sounds on all MIDI channels
+ for (iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ iter->stopSounds();
+}
+
+bool SoundGen2GS::playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample) {
+ stopSounds();
+ IIgsMidiChannel &channel = _midiChannels[kSfxMidiChannel];
+
+ channel.setInstrument(&sampleHeader.instrument, sample);
+ channel.setVolume(sampleHeader.volume);
+ channel.noteOn(sampleHeader.pitch, 64); // Use default velocity (i.e. 64)
+
+ return true;
+}
+
+void SoundGen2GS::playMidiSound() {
+ if (_disabledMidi)
+ return;
+
+ const uint8 *p;
+ uint8 parm1, parm2;
+ static uint8 cmd, ch;
+
+ if (_playingSound == -1 || _vm->_game.sounds[_playingSound] == NULL) {
+ warning("Error playing Apple IIGS MIDI sound resource");
+ _playing = false;
+
+ return;
+ }
+
+ IIgsMidi *midiObj = (IIgsMidi *) _vm->_game.sounds[_playingSound];
+
+ _playing = true;
+ p = midiObj->getPtr();
+
+ midiObj->_soundBufTicks++;
+
+ while (true) {
+ uint8 readByte = *p;
+
+ // Check for end of MIDI sequence marker (Can also be here before delta-time)
+ if (readByte == MIDI_BYTE_STOP_SEQUENCE) {
+ debugC(3, kDebugLevelSound, "End of MIDI sequence (Before reading delta-time)");
+ _playing = false;
+
+ midiObj->rewind();
+
+ return;
+ } else if (readByte == MIDI_BYTE_TIMER_SYNC) {
+ debugC(3, kDebugLevelSound, "Timer sync");
+ p++; // Jump over the timer sync byte as it's not needed
+
+ continue;
+ }
+
+ uint8 deltaTime = readByte;
+ if (midiObj->_midiTicks + deltaTime > midiObj->_soundBufTicks) {
+ break;
+ }
+ midiObj->_midiTicks += deltaTime;
+ p++; // Jump over the delta-time byte as it was already taken care of
+
+ // Check for end of MIDI sequence marker (This time it after reading delta-time)
+ if (*p == MIDI_BYTE_STOP_SEQUENCE) {
+ debugC(3, kDebugLevelSound, "End of MIDI sequence (After reading delta-time)");
+ _playing = false;
+
+ midiObj->rewind();
+
+ return;
+ }
+
+ // Separate byte into command and channel if it's a command byte.
+ // Otherwise use running status (i.e. previously set command and channel).
+ if (*p & 0x80) {
+ cmd = *p++;
+ ch = cmd & 0x0f;
+ cmd >>= 4;
+ }
+
+ switch (cmd) {
+ case MIDI_CMD_NOTE_OFF:
+ parm1 = *p++;
+ parm2 = *p++;
+ midiNoteOff(ch, parm1, parm2);
+ break;
+ case MIDI_CMD_NOTE_ON:
+ parm1 = *p++;
+ parm2 = *p++;
+ midiNoteOn(ch, parm1, parm2);
+ break;
+ case MIDI_CMD_CONTROLLER:
+ parm1 = *p++;
+ parm2 = *p++;
+ midiController(ch, parm1, parm2);
+ break;
+ case MIDI_CMD_PROGRAM_CHANGE:
+ parm1 = *p++;
+ midiProgramChange(ch, parm1);
+ break;
+ case MIDI_CMD_PITCH_WHEEL:
+ parm1 = *p++;
+ parm2 = *p++;
+
+ uint16 wheelPos = ((parm2 & 0x7F) << 7) | (parm1 & 0x7F); // 14-bit value
+ midiPitchWheel(wheelPos);
+ break;
+ }
+ }
+
+ midiObj->setPtr(p);
+}
+
+void SoundGen2GS::midiNoteOff(uint8 channel, uint8 note, uint8 velocity) {
+ _midiChannels[channel].noteOff(note, velocity);
+ debugC(3, kDebugLevelSound, "note off, channel %02x, note %02x, velocity %02x", channel, note, velocity);
+}
+
+void SoundGen2GS::midiNoteOn(uint8 channel, uint8 note, uint8 velocity) {
+ _midiChannels[channel].noteOn(note, velocity);
+ debugC(3, kDebugLevelSound, "note on, channel %02x, note %02x, velocity %02x", channel, note, velocity);
+}
+
+// TODO: Check if controllers behave differently on different MIDI channels
+// TODO: Doublecheck what other controllers than the volume controller do
+void SoundGen2GS::midiController(uint8 channel, uint8 controller, uint8 value) {
+ IIgsMidiChannel &midiChannel = _midiChannels[channel];
+
+ // The tested Apple IIGS AGI MIDI resources only used
+ // controllers 0 (Bank select?), 7 (Volume) and 64 (Sustain On/Off).
+ // Controller 0's parameter was in range 94-127,
+ // controller 7's parameter was in range 0-127 and
+ // controller 64's parameter was always 0 (i.e. sustain off).
+ bool unimplemented = false;
+ switch (controller) {
+ case 7: // Volume
+ midiChannel.setVolume(value);
+ break;
+ default:
+ unimplemented = true;
+ break;
+ }
+ debugC(3, kDebugLevelSound, "controller %02x, ch %02x, val %02x%s", controller, channel, value, unimplemented ? " (Unimplemented)" : "");
+}
+
+void SoundGen2GS::midiProgramChange(uint8 channel, uint8 program) {
+ _midiChannels[channel].setInstrument(getInstrument(program), _wave.begin());
+ debugC(3, kDebugLevelSound, "program change %02x, channel %02x", program, channel);
+}
+
+void SoundGen2GS::midiPitchWheel(uint8 wheelPos) {
+ // In all the tested Apple IIGS AGI MIDI resources
+ // pitch wheel commands always used 0x2000 (Center position).
+ // Therefore it should be quite safe to ignore this command.
+ debugC(3, kDebugLevelSound, "pitch wheel position %04x (Unimplemented)", wheelPos);
+}
+
+const IIgsInstrumentHeader* SoundGen2GS::getInstrument(uint8 program) const {
+ return &_instruments[_midiProgToInst->map(program)];
+}
+
+void SoundGen2GS::setProgramChangeMapping(const MidiProgramChangeMapping *mapping) {
+ _midiProgToInst = mapping;
+}
+
+void SoundGen2GS::removeStoppedSounds() {
+ for (Common::Array<IIgsMidiChannel>::iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ iter->removeStoppedSounds();
+}
+
+uint SoundGen2GS::activeSounds() const {
+ uint result = 0;
+
+ for (Common::Array<IIgsMidiChannel>::const_iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ result += iter->activeSounds();
+
+ return result;
+}
+
+IIgsMidi::IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ _data = data; // Save the resource pointer
+ _ptr = _data + 2; // Set current position to just after the header
+ _len = len; // Save the resource's length
+ _type = READ_LE_UINT16(data); // Read sound resource's type
+ _midiTicks = _soundBufTicks = 0;
+ _isValid = (_type == AGI_SOUND_MIDI) && (_data != NULL) && (_len >= 2);
+
+ if (!_isValid) // Check for errors
+ warning("Error creating Apple IIGS midi sound from resource %d (Type %d, length %d)", resnum, _type, len);
+}
+
+/**
+ * Convert sample from 8-bit unsigned to 8-bit signed format.
+ * @param source Source stream containing the 8-bit unsigned sample data.
+ * @param dest Destination buffer for the 8-bit signed sample data.
+ * @param length Length of the sample data to be converted.
+ */
+static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint length) {
+ // Convert the wave from 8-bit unsigned to 8-bit signed format
+ for (uint i = 0; i < length; i++)
+ dest[i] = (int8) ((int) source.readByte() - 128);
+ return !(source.eos() || source.err());
+}
+
+IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES);
+
+ // Check that the header was read ok and that it's of the correct type
+ if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource
+ uint32 sampleStartPos = stream.pos();
+ uint32 tailLen = stream.size() - sampleStartPos;
+
+ if (tailLen < _header.sampleSize) { // Check if there's no room for the sample data in the stream
+ // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes
+ // of sample data although header says it should have 16384 bytes.
+ warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left",
+ resnum, tailLen, _header.sampleSize);
+
+ _header.sampleSize = (uint16) tailLen; // Use the part that's left
+ }
+
+ if (_header.pitch > 0x7F) { // Check if the pitch is invalid
+ warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, _header.pitch);
+
+ _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too
+ }
+
+ // Finalize the header info using the 8-bit unsigned sample data
+ _header.finalize(stream);
+
+ // Convert sample data from 8-bit unsigned to 8-bit signed format
+ stream.seek(sampleStartPos);
+ _sample = new int8[_header.sampleSize];
+
+ if (_sample != NULL)
+ _isValid = convertWave(stream, _sample, _header.sampleSize);
+ }
+
+ if (!_isValid) // Check for errors
+ warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len);
+}
+
+/** Reads an Apple IIGS envelope from then given stream. */
+bool IIgsEnvelope::read(Common::SeekableReadStream &stream) {
+ for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) {
+ seg[segNum].bp = stream.readByte();
+ seg[segNum].inc = stream.readUint16LE();
+ }
+
+ return !(stream.eos() || stream.err());
+}
+
+/** Reads an Apple IIGS wave information structure from the given stream. */
+bool IIgsWaveInfo::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
+ top = stream.readByte();
+ addr = stream.readByte() * 256;
+ size = (1 << (stream.readByte() & 7)) * 256;
+
+ // Read packed mode byte and parse it into parts
+ byte packedModeByte = stream.readByte();
+ channel = (packedModeByte >> 4) & 1; // Bit 4
+ mode = (packedModeByte >> 1) & 3; // Bits 1-2
+ halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean)
+
+ relPitch = stream.readSint16LE();
+
+ // Zero the wave address if we want to ignore the wave address info
+ if (ignoreAddr)
+ addr = 0;
+
+ return !(stream.eos() || stream.err());
+}
+
+bool IIgsWaveInfo::finalize(Common::SeekableReadStream &uint8Wave) {
+ uint32 startPos = uint8Wave.pos(); // Save stream's starting position
+ uint8Wave.seek(addr, SEEK_CUR); // Seek to wave's address
+
+ // Calculate the true sample size (A zero ends the sample prematurely)
+ uint trueSize = size; // Set a default value for the result
+ for (uint i = 0; i < size; i++) {
+ if (uint8Wave.readByte() == 0) {
+ trueSize = i;
+ // A zero in the sample stream turns off looping
+ // (At least that's what MESS 0.117 and KEGS32 0.91 seem to do)
+ if (mode == OSC_MODE_LOOP)
+ mode = OSC_MODE_ONESHOT;
+ break;
+ }
+ }
+ size = trueSize; // Set the true sample size
+
+ uint8Wave.seek(startPos); // Seek back to the stream's starting position
+
+ return true;
+}
+
+bool IIgsOscillator::finalize(Common::SeekableReadStream &uint8Wave) {
+ for (uint i = 0; i < WAVES_PER_OSCILLATOR; i++)
+ if (!waves[i].finalize(uint8Wave))
+ return false;
+
+ return true;
+}
+
+bool IIgsOscillatorList::read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr) {
+ // First read the A waves and then the B waves for the oscillators
+ for (uint waveNum = 0; waveNum < WAVES_PER_OSCILLATOR; waveNum++)
+ for (uint oscNum = 0; oscNum < oscillatorCount; oscNum++)
+ if (!osc[oscNum].waves[waveNum].read(stream, ignoreAddr))
+ return false;
+
+ count = oscillatorCount; // Set the oscillator count
+
+ return true;
+}
+
+bool IIgsOscillatorList::finalize(Common::SeekableReadStream &uint8Wave) {
+ for (uint i = 0; i < count; i++)
+ if (!osc[i].finalize(uint8Wave))
+ return false;
+
+ return true;
+}
+
+bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
+ env.read(stream);
+ relseg = stream.readByte();
+ /*byte priority =*/ stream.readByte(); // Not needed? 32 in all tested data.
+ bendrange = stream.readByte();
+ vibdepth = stream.readByte();
+ vibspeed = stream.readByte();
+ /*byte spare =*/ stream.readByte(); // Not needed? 0 in all tested data.
+ byte wac = stream.readByte(); // Read A wave count
+ byte wbc = stream.readByte(); // Read B wave count
+ oscList.read(stream, wac, ignoreAddr); // Read the oscillators
+ return (wac == wbc) && !(stream.eos() || stream.err()); // A and B wave counts must match
+}
+
+bool IIgsInstrumentHeader::finalize(Common::SeekableReadStream &uint8Wave) {
+ return oscList.finalize(uint8Wave);
+}
+
+bool IIgsSampleHeader::read(Common::SeekableReadStream &stream) {
+ type = stream.readUint16LE();
+ pitch = stream.readByte();
+ unknownByte_Ofs3 = stream.readByte();
+ volume = stream.readByte();
+ unknownByte_Ofs5 = stream.readByte();
+ instrumentSize = stream.readUint16LE();
+ sampleSize = stream.readUint16LE();
+ // Read the instrument header *ignoring* its wave address info
+
+ return instrument.read(stream, true);
+}
+
+bool IIgsSampleHeader::finalize(Common::SeekableReadStream &uint8Wave) {
+ return instrument.finalize(uint8Wave);
+}
+
+void IIgsMidiChannel::stopSounds() {
+ // Stops all sounds on this single MIDI channel
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ iter->stop();
+
+ _gsChannels.clear();
+}
+
+void IIgsMidiChannel::removeStoppedSounds() {
+ for (int i = _gsChannels.size() - 1; i >= 0; i--)
+ if (!_gsChannels[i].playing())
+ _gsChannels.remove_at(i);
+}
+
+uint IIgsMidiChannel::activeSounds() const {
+ uint result = 0;
+
+ for (const_iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ if (!iter->end)
+ result++;
+
+ return result;
+}
+
+void IIgsMidiChannel::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
+ _instrument = instrument;
+ _sample = sample;
+
+ // Set program on each Apple IIGS channel playing on this MIDI channel
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ iter->setInstrument(instrument, sample);
+}
+
+void IIgsMidiChannel::setVolume(uint8 volume) {
+ _volume = volume;
+
+ // Set volume on each Apple IIGS channel playing on this MIDI channel
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ iter->setChannelVolume(volume);
+}
+
+void IIgsMidiChannel::noteOff(uint8 note, uint8 velocity) {
+ // Go through all the notes playing on this MIDI channel
+ // and turn off the ones that are playing the given note
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ if (iter->origNote == note)
+ iter->noteOff(velocity);
+}
+
+void IIgsMidiChannel::noteOn(uint8 note, uint8 velocity) {
+ IIgsChannelInfo channel;
+
+ // Use the default channel volume and instrument
+ channel.setChannelVolume(_volume);
+ channel.setInstrument(_instrument, _sample);
+
+ // Set the note on and save the channel
+ channel.noteOn(note, velocity);
+ _gsChannels.push_back(channel);
+}
+
+void IIgsChannelInfo::rewind() {
+ this->envVol = this->startEnvVol;
+ this->envSeg = 0;
+ this->pos = intToFrac(0);
+}
+
+void IIgsChannelInfo::setChannelVolume(uint8 volume) {
+ this->chanVol = intToFrac(volume);
+}
+
+void IIgsChannelInfo::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
+ assert(instrument != NULL && sample != NULL);
+ this->ins = instrument;
+ this->unrelocatedSample = sample;
+}
+
+// TODO/FIXME: Implement correctly and fully (Take velocity into account etc)
+void IIgsChannelInfo::noteOn(uint8 noteParam, uint8 velocity) {
+ this->origNote = noteParam;
+ this->startEnvVol = intToFrac(0);
+ rewind();
+
+ const IIgsWaveInfo *waveInfo = NULL;
+
+ for (uint i = 0; i < ins->oscList.count; i++)
+ if (ins->oscList(i).waves[0].top >= noteParam)
+ waveInfo = &ins->oscList(i).waves[0];
+
+ assert(waveInfo != NULL);
+
+ this->relocatedSample = this->unrelocatedSample + waveInfo->addr;
+ this->posAdd = intToFrac(0);
+ this->note = intToFrac(noteParam) + doubleToFrac(waveInfo->relPitch/256.0);
+ this->vol = doubleToFrac(fracToDouble(this->envVol) * fracToDouble(this->chanVol) / 127.0);
+ this->loop = (waveInfo->mode == OSC_MODE_LOOP);
+ this->size = waveInfo->size - waveInfo->addr;
+ this->end = waveInfo->halt;
+}
+
+// TODO/FIXME: Implement correctly and fully (Take release time and velocity into account etc)
+void IIgsChannelInfo::noteOff(uint8 velocity) {
+ this->loop = false;
+ this->envSeg = ins->relseg;
+}
+
+void IIgsChannelInfo::stop() {
+ this->end = true;
+}
+
+bool IIgsChannelInfo::playing() {
+ return !this->end;
+}
+
+/**
+ * A function object (i.e. a functor) for testing if a Common::FSNode
+ * object's name is equal (Ignoring case) to a string or to at least
+ * one of the strings in a list of strings. Can be used e.g. with find_if().
+ */
+struct fsnodeNameEqualsIgnoreCase : public Common::UnaryFunction<const Common::FSNode&, bool> {
+// FIXME: This should be replaced; use SearchMan instead
+ fsnodeNameEqualsIgnoreCase(const Common::StringArray &str) : _str(str) {}
+ fsnodeNameEqualsIgnoreCase(const Common::String str) { _str.push_back(str); }
+ bool operator()(const Common::FSNode &param) const {
+ for (Common::StringArray::const_iterator iter = _str.begin(); iter != _str.end(); ++iter)
+ if (param.getName().equalsIgnoreCase(*iter))
+ return true;
+ return false;
+ }
+private:
+ Common::StringArray _str;
+};
+
+bool SoundGen2GS::loadInstruments() {
+ // Check that the platform is Apple IIGS, as only it uses custom instruments
+ if (_vm->getPlatform() != Common::kPlatformApple2GS) {
+ debugC(3, kDebugLevelSound, "Platform isn't Apple IIGS so not loading any instruments");
+ return true;
+ }
+
+ // Get info on the particular Apple IIGS AGI game's executable
+ const IIgsExeInfo *exeInfo = getIIgsExeInfo((enum AgiGameID) _vm->getGameID());
+ if (exeInfo == NULL) {
+ warning("Unsupported Apple IIGS game, not loading instruments");
+ return false;
+ }
+
+ // List files in the game path
+ Common::FSList fslist;
+ Common::FSNode dir(ConfMan.get("path"));
+ if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) {
+ warning("Invalid game path (\"%s\"), not loading Apple IIGS instruments", dir.getPath().c_str());
+ return false;
+ }
+
+ // Populate executable filenames list (Long filename and short filename) for searching
+ Common::StringArray exeNames;
+ exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS16");
+ exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS");
+
+ // Populate wave filenames list (Long filename and short filename) for searching
+ Common::StringArray waveNames;
+ waveNames.push_back("SIERRASTANDARD");
+ waveNames.push_back("SIERRAST");
+
+ // Search for the executable file and the wave file (i.e. check if any of the filenames match)
+ Common::FSList::const_iterator exeFsnode, waveFsnode;
+ exeFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(exeNames));
+ waveFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(waveNames));
+
+ // Make sure that we found the executable file
+ if (exeFsnode == fslist.end()) {
+ warning("Couldn't find Apple IIGS game executable (%s), not loading instruments", exeNames.begin()->c_str());
+ return false;
+ }
+
+ // Make sure that we found the wave file
+ if (waveFsnode == fslist.end()) {
+ warning("Couldn't find Apple IIGS wave file (%s), not loading instruments", waveNames.begin()->c_str());
+ return false;
+ }
+
+ // Set the MIDI program change to instrument number mapping and
+ // load the instrument headers and their sample data.
+ // None of the tested SIERRASTANDARD-files have zeroes in them so
+ // there's no need to check for prematurely ending samples here.
+ setProgramChangeMapping(&exeInfo->instSet.progToInst);
+ return loadWaveFile(*waveFsnode, *exeInfo) && loadInstrumentHeaders(*exeFsnode, *exeInfo);
+}
+
+/** Older Apple IIGS AGI MIDI program change to instrument number mapping. */
+static const MidiProgramChangeMapping progToInstMappingV1 = {
+ {19, 20, 22, 23, 21, 24, 5, 5, 5, 5,
+ 6, 7, 10, 9, 11, 9, 15, 8, 5, 5,
+ 17, 16, 18, 12, 14, 5, 5, 5, 5, 5,
+ 0, 1, 2, 9, 3, 4, 15, 2, 2, 2,
+ 25, 13, 13, 25},
+ 5
+};
+
+/** Newer Apple IIGS AGI MIDI program change to instrument number mapping. */
+static const MidiProgramChangeMapping progToInstMappingV2 = {
+ {21, 22, 24, 25, 23, 26, 6, 6, 6, 6,
+ 7, 9, 12, 8, 13, 11, 17, 10, 6, 6,
+ 19, 18, 20, 14, 16, 6, 6, 6, 6, 6,
+ 0, 1, 2, 4, 3, 5, 17, 2, 2, 2,
+ 27, 15, 15, 27},
+ 6
+};
+
+/** Older Apple IIGS AGI instrument set. Used only by Space Quest I (AGI v1.002). */
+static const InstrumentSetInfo instSetV1 = {
+ 1192, 26, "7ee16bbc135171ffd6b9120cc7ff1af2", "edd3bf8905d9c238e02832b732fb2e18", progToInstMappingV1
+};
+
+/** Newer Apple IIGS AGI instrument set (AGI v1.003+). Used by all others than Space Quest I. */
+static const InstrumentSetInfo instSetV2 = {
+ 1292, 28, "b7d428955bb90721996de1cbca25e768", "c05fb0b0e11deefab58bc68fbd2a3d07", progToInstMappingV2
+};
+
+/** Information about different Apple IIGS AGI executables. */
+static const IIgsExeInfo IIgsExeInfos[] = {
+ {GID_SQ1, "SQ", 0x1002, 138496, 0x80AD, instSetV1},
+ {GID_LSL1, "LL", 0x1003, 141003, 0x844E, instSetV2},
+ {GID_AGIDEMO, "DEMO", 0x1005, 141884, 0x8469, instSetV2},
+ {GID_KQ1, "KQ", 0x1006, 141894, 0x8469, instSetV2},
+ {GID_PQ1, "PQ", 0x1007, 141882, 0x8469, instSetV2},
+ {GID_MIXEDUP, "MG", 0x1013, 142552, 0x84B7, instSetV2},
+ {GID_KQ2, "KQ2", 0x1013, 143775, 0x84B7, instSetV2},
+ {GID_KQ3, "KQ3", 0x1014, 144312, 0x84B7, instSetV2},
+ {GID_SQ2, "SQ2", 0x1014, 107882, 0x6563, instSetV2},
+ {GID_MH1, "MH", 0x2004, 147678, 0x8979, instSetV2},
+ {GID_KQ4, "KQ4", 0x2006, 147652, 0x8979, instSetV2},
+ {GID_BC, "BC", 0x3001, 148192, 0x8979, instSetV2},
+ {GID_GOLDRUSH, "GR", 0x3003, 148268, 0x8979, instSetV2}
+};
+
+/**
+ * Finds information about an Apple IIGS AGI executable based on the game ID.
+ * @return A non-null IIgsExeInfo pointer if successful, otherwise NULL.
+ */
+const IIgsExeInfo *SoundGen2GS::getIIgsExeInfo(enum AgiGameID gameid) const {
+ for (int i = 0; i < ARRAYSIZE(IIgsExeInfos); i++)
+ if (IIgsExeInfos[i].gameid == gameid)
+ return &IIgsExeInfos[i];
+ return NULL;
+}
+
+bool SoundGen2GS::loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo) {
+ bool loadedOk = false; // Was loading successful?
+ Common::File file;
+
+ // Open the executable file and check that it has correct size
+ file.open(exePath);
+ if (file.size() != (int32)exeInfo.exeSize) {
+ debugC(3, kDebugLevelSound, "Apple IIGS executable (%s) has wrong size (Is %d, should be %d)",
+ exePath.getPath().c_str(), file.size(), exeInfo.exeSize);
+ }
+
+ // Read the whole executable file into memory
+ Common::SharedPtr<Common::MemoryReadStream> data(file.readStream(file.size()));
+ file.close();
+
+ // Check that we got enough data to be able to parse the instruments
+ if (data && data->size() >= (int32)(exeInfo.instSetStart + exeInfo.instSet.byteCount)) {
+ // Check instrument set's length (The info's saved in the executable)
+ data->seek(exeInfo.instSetStart - 4);
+ uint16 instSetByteCount = data->readUint16LE();
+ if (instSetByteCount != exeInfo.instSet.byteCount) {
+ debugC(3, kDebugLevelSound, "Wrong instrument set size (Is %d, should be %d) in Apple IIGS executable (%s)",
+ instSetByteCount, exeInfo.instSet.byteCount, exePath.getPath().c_str());
+ }
+
+ // Check instrument set's md5sum
+ data->seek(exeInfo.instSetStart);
+
+ char md5str[32+1];
+ Common::md5_file_string(*data, md5str, exeInfo.instSet.byteCount);
+ if (scumm_stricmp(md5str, exeInfo.instSet.md5)) {
+ warning("Unknown Apple IIGS instrument set (md5: %s) in %s, trying to use it nonetheless",
+ md5str, exePath.getPath().c_str());
+ }
+
+ // Read in the instrument set one instrument at a time
+ data->seek(exeInfo.instSetStart);
+
+ // Load the instruments
+ _instruments.clear();
+ _instruments.reserve(exeInfo.instSet.instCount);
+
+ IIgsInstrumentHeader instrument;
+ for (uint i = 0; i < exeInfo.instSet.instCount; i++) {
+ if (!instrument.read(*data)) {
+ warning("Error loading Apple IIGS instrument (%d. of %d) from %s, not loading more instruments",
+ i + 1, exeInfo.instSet.instCount, exePath.getPath().c_str());
+ break;
+ }
+ _instruments.push_back(instrument); // Add the successfully loaded instrument to the instruments array
+ }
+
+ // Loading was successful only if all instruments were loaded successfully
+ loadedOk = (_instruments.size() == exeInfo.instSet.instCount);
+ } else // Couldn't read enough data from the executable file
+ warning("Error loading instruments from Apple IIGS executable (%s)", exePath.getPath().c_str());
+
+ return loadedOk;
+}
+
+bool SoundGen2GS::loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo) {
+ Common::File file;
+
+ // Open the wave file and read it into memory
+ file.open(wavePath);
+ Common::SharedPtr<Common::MemoryReadStream> uint8Wave(file.readStream(file.size()));
+ file.close();
+
+ // Check that we got the whole wave file
+ if (uint8Wave && uint8Wave->size() == SIERRASTANDARD_SIZE) {
+ // Check wave file's md5sum
+ char md5str[32+1];
+ Common::md5_file_string(*uint8Wave, md5str, SIERRASTANDARD_SIZE);
+ if (scumm_stricmp(md5str, exeInfo.instSet.waveFileMd5)) {
+ warning("Unknown Apple IIGS wave file (md5: %s, game: %s).\n" \
+ "Please report the information on the previous line to the ScummVM team.\n" \
+ "Using the wave file as it is - music may sound weird", md5str, exeInfo.exePrefix);
+ }
+
+ uint8Wave->seek(0); // Seek wave to its start
+ // Convert the wave file from 8-bit unsigned to 8-bit signed and save the result
+ _wave.resize(uint8Wave->size());
+ return convertWave(*uint8Wave, _wave.begin(), uint8Wave->size());
+ } else { // Couldn't read the wave file or it had incorrect size
+ warning("Error loading Apple IIGS wave file (%s), not loading instruments", wavePath.getPath().c_str());
+ return false;
+ }
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_2gs.h b/engines/agi/sound_2gs.h
new file mode 100644
index 0000000000..12dede0b69
--- /dev/null
+++ b/engines/agi/sound_2gs.h
@@ -0,0 +1,353 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 AGI_SOUND_2GS_H
+#define AGI_SOUND_2GS_H
+
+#include "common/frac.h"
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+#define BUFFER_SIZE 410
+
+// Apple IIGS MIDI uses 60 ticks per second (Based on tests with Apple IIGS
+// KQ1 and SQ1 under MESS 0.124a). So we make the audio buffer size to be a
+// 1/60th of a second in length. That should be getSampleRate() / 60 samples
+// in length but as getSampleRate() is always 22050 at the moment we just use
+// the hardcoded value of 368 (22050/60 = 367.5 which rounds up to 368).
+// FIXME: Use getSampleRate() / 60 rather than a hardcoded value
+#define IIGS_BUFFER_SIZE 368
+
+// MIDI command values (Shifted right by 4 so they're in the lower nibble)
+#define MIDI_CMD_NOTE_OFF 0x08
+#define MIDI_CMD_NOTE_ON 0x09
+#define MIDI_CMD_CONTROLLER 0x0B
+#define MIDI_CMD_PROGRAM_CHANGE 0x0C
+#define MIDI_CMD_PITCH_WHEEL 0x0E
+// Whole MIDI byte values (Command and channel info together)
+#define MIDI_BYTE_STOP_SEQUENCE 0xFC
+#define MIDI_BYTE_TIMER_SYNC 0xF8
+
+struct IIgsEnvelopeSegment {
+ uint8 bp;
+ uint16 inc; ///< 8b.8b fixed point, very probably little endian
+};
+
+#define ENVELOPE_SEGMENT_COUNT 8
+struct IIgsEnvelope {
+ IIgsEnvelopeSegment seg[ENVELOPE_SEGMENT_COUNT];
+
+ /** Reads an Apple IIGS envelope from then given stream. */
+ bool read(Common::SeekableReadStream &stream);
+};
+
+// 2**(1/12) i.e. the 12th root of 2
+#define SEMITONE 1.059463094359295
+
+// C6's frequency is A4's (440 Hz) frequency but one full octave and three semitones higher
+// i.e. C6_FREQ = 440 * pow(2.0, 15/12.0)
+#define C6_FREQ 1046.502261202395
+
+// Size of the SIERRASTANDARD file (i.e. the wave file i.e. the sample data used by the instruments).
+#define SIERRASTANDARD_SIZE 65536
+
+// Maximum number of instruments in an Apple IIGS instrument set.
+// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
+#define MAX_INSTRUMENTS 28
+
+struct IIgsWaveInfo {
+ uint8 top;
+ uint addr;
+ uint size;
+// Oscillator channel
+#define OSC_CHANNEL_RIGHT 0
+#define OSC_CHANNEL_LEFT 1
+ uint channel;
+// Oscillator mode
+#define OSC_MODE_LOOP 0
+#define OSC_MODE_ONESHOT 1
+#define OSC_MODE_SYNC_AM 2
+#define OSC_MODE_SWAP 3
+ uint mode;
+ bool halt;
+ int16 relPitch; ///< Relative pitch in semitones (Signed 8b.8b fixed point)
+
+ /** Reads an Apple IIGS wave information structure from the given stream. */
+ bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+// Number of waves per Apple IIGS sound oscillator
+#define WAVES_PER_OSCILLATOR 2
+
+/** An Apple IIGS sound oscillator. Consists always of two waves. */
+struct IIgsOscillator {
+ IIgsWaveInfo waves[WAVES_PER_OSCILLATOR];
+
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+// Maximum number of oscillators in an Apple IIGS instrument.
+// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
+#define MAX_OSCILLATORS 4
+
+/** An Apple IIGS sound oscillator list. */
+struct IIgsOscillatorList {
+ uint count; ///< Oscillator count
+ IIgsOscillator osc[MAX_OSCILLATORS]; ///< The oscillators
+
+ /** Indexing operators for easier access to the oscillators. */
+ const IIgsOscillator &operator()(uint index) const { return osc[index]; }
+ IIgsOscillator &operator()(uint index) { return osc[index]; }
+
+ /** Reads an Apple IIGS oscillator list from the given stream. */
+ bool read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr = false);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+struct IIgsInstrumentHeader {
+ IIgsEnvelope env;
+ uint8 relseg;
+ uint8 bendrange;
+ uint8 vibdepth;
+ uint8 vibspeed;
+ IIgsOscillatorList oscList;
+
+ /**
+ * Read an Apple IIGS instrument header from the given stream.
+ * @param stream The source stream from which to read the data.
+ * @param ignoreAddr Should we ignore wave infos' wave address variable's value?
+ * @return True if successful, false otherwise.
+ */
+ bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+struct IIgsSampleHeader {
+ uint16 type;
+ uint8 pitch; ///< Logarithmic, base is 2**(1/12), unknown multiplier (Possibly in range 1040-1080)
+ uint8 unknownByte_Ofs3; // 0x7F in Gold Rush's sound resource 60, 0 in all others.
+ uint8 volume; ///< Current guess: Logarithmic in 6 dB steps
+ uint8 unknownByte_Ofs5; ///< 0 in all tested samples.
+ uint16 instrumentSize; ///< Little endian. 44 in all tested samples. A guess.
+ uint16 sampleSize; ///< Little endian. Accurate in all tested samples excluding Manhunter I's sound resource 16.
+ IIgsInstrumentHeader instrument;
+
+ /**
+ * Read an Apple IIGS AGI sample header from the given stream.
+ * @param stream The source stream from which to read the data.
+ * @return True if successful, false otherwise.
+ */
+ bool read(Common::SeekableReadStream &stream);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+struct IIgsChannelInfo {
+ const IIgsInstrumentHeader *ins; ///< Instrument info
+ const int8 *relocatedSample; ///< Source sample data (8-bit signed format) using relocation
+ const int8 *unrelocatedSample; ///< Source sample data (8-bit signed format) without relocation
+ frac_t pos; ///< Current sample position
+ frac_t posAdd; ///< Current sample position adder (Calculated using note, vibrato etc)
+ uint8 origNote; ///< The original note without the added relative pitch
+ frac_t note; ///< Note (With the added relative pitch)
+ frac_t vol; ///< Current volume (Takes both channel volume and enveloping into account)
+ frac_t chanVol; ///< Channel volume
+ frac_t startEnvVol; ///< Starting envelope volume
+ frac_t envVol; ///< Current envelope volume
+ uint envSeg; ///< Current envelope segment
+ uint size; ///< Sample size
+ bool loop; ///< Should we loop the sample?
+ bool end; ///< Has the playing ended?
+
+ void rewind(); ///< Rewinds the sound playing on this channel to its start
+ void setChannelVolume(uint8 volume); ///< Sets the channel volume
+ void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample); ///< Sets the instrument to be used on this channel
+ void noteOn(uint8 noteParam, uint8 velocity); ///< Starts playing a note on this channel
+ void noteOff(uint8 velocity); ///< Releases the note on this channel
+ void stop(); ///< Stops the note playing on this channel instantly
+ bool playing(); ///< Is there a note playing on this channel?
+};
+
+class IIgsMidi : public AgiSound {
+public:
+ IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~IIgsMidi() { if (_data != NULL) free(_data); }
+ virtual uint16 type() { return _type; }
+ virtual const uint8 *getPtr() { return _ptr; }
+ virtual void setPtr(const uint8 *ptr) { _ptr = ptr; }
+ virtual void rewind() { _ptr = _data + 2; _midiTicks = _soundBufTicks = 0; }
+protected:
+ uint8 *_data; ///< Raw sound resource data
+ const uint8 *_ptr; ///< Pointer to the current position in the MIDI data
+ uint32 _len; ///< Length of the raw sound resource
+ uint16 _type; ///< Sound resource type
+public:
+ uint _midiTicks; ///< MIDI song position in ticks (1/60ths of a second)
+ uint _soundBufTicks; ///< Sound buffer position in ticks (1/60ths of a second)
+};
+
+class IIgsSample : public AgiSound {
+public:
+ IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~IIgsSample() { delete[] _sample; }
+ virtual uint16 type() { return _header.type; }
+ const IIgsSampleHeader &getHeader() const { return _header; }
+ const int8 *getSample() const { return _sample; }
+protected:
+ IIgsSampleHeader _header; ///< Apple IIGS AGI sample header
+ int8 *_sample; ///< Sample data (8-bit signed format)
+};
+
+/** Apple IIGS MIDI program change to instrument number mapping. */
+struct MidiProgramChangeMapping {
+ byte midiProgToInst[44]; ///< Lookup table for the MIDI program number to instrument number mapping
+ byte undefinedInst; ///< The undefined instrument number
+
+ // Maps the MIDI program number to an instrument number
+ byte map(uint midiProg) const {
+ return midiProg < ARRAYSIZE(midiProgToInst) ? midiProgToInst[midiProg] : undefinedInst;
+ }
+};
+
+/** Apple IIGS AGI instrument set information. */
+struct InstrumentSetInfo {
+ uint byteCount; ///< Length of the whole instrument set in bytes
+ uint instCount; ///< Amount of instrument in the set
+ const char *md5; ///< MD5 hex digest of the whole instrument set
+ const char *waveFileMd5; ///< MD5 hex digest of the wave file (i.e. the sample data used by the instruments)
+ const MidiProgramChangeMapping &progToInst; ///< Program change to instrument number mapping
+};
+
+/** Apple IIGS AGI executable file information. */
+struct IIgsExeInfo {
+ enum AgiGameID gameid; ///< Game ID
+ const char *exePrefix; ///< Prefix of the Apple IIGS AGI executable (e.g. "SQ", "PQ", "KQ4" etc)
+ uint agiVer; ///< Apple IIGS AGI version number, not strictly needed
+ uint exeSize; ///< Size of the Apple IIGS AGI executable file in bytes
+ uint instSetStart; ///< Starting offset of the instrument set inside the executable file
+ const InstrumentSetInfo &instSet; ///< Information about the used instrument set
+};
+
+class IIgsMidiChannel {
+public:
+ IIgsMidiChannel() : _instrument(0), _sample(0), _volume(0) {}
+ uint activeSounds() const; ///< How many active sounds are playing?
+ void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample);
+ void setVolume(uint8 volume);
+ void noteOff(uint8 note, uint8 velocity);
+ void noteOn(uint8 note, uint8 velocity);
+ void stopSounds(); ///< Clears the channel of any sounds
+ void removeStoppedSounds(); ///< Removes all stopped sounds from this MIDI channel
+public:
+ typedef Common::Array<IIgsChannelInfo>::const_iterator const_iterator;
+ typedef Common::Array<IIgsChannelInfo>::iterator iterator;
+ Common::Array<IIgsChannelInfo> _gsChannels; ///< Apple IIGS channels playing on this MIDI channel
+protected:
+ const IIgsInstrumentHeader *_instrument; ///< Instrument used on this MIDI channel
+ const int8 *_sample; ///< Sample data used on this MIDI channel
+ uint8 _volume; ///< MIDI controller number 7 (Volume)
+};
+
+class SoundGen2GS : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGen2GS(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGen2GS();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+
+private:
+ bool _disabledMidi;
+ int _playingSound;
+ bool _playing;
+
+ int16 *_sndBuffer;
+
+/**
+ * Class for managing Apple IIGS sound channels.
+ * TODO: Check what instruments are used by default on the MIDI channels
+ * FIXME: Some instrument choices sound wrong
+ */
+private:
+ typedef Common::Array<IIgsMidiChannel>::const_iterator const_iterator;
+ typedef Common::Array<IIgsMidiChannel>::iterator iterator;
+ static const uint kSfxMidiChannel = 0; ///< The MIDI channel used for playing sound effects
+
+ bool loadInstruments();
+ const IIgsExeInfo *getIIgsExeInfo(enum AgiGameID gameid) const;
+
+ void setProgramChangeMapping(const MidiProgramChangeMapping *mapping);
+ bool loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo);
+ bool loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo);
+
+ // Miscellaneous methods
+ void fillAudio(int16 *stream, uint len);
+ uint32 mixSound();
+ void playSound();
+ uint activeSounds() const; ///< How many active sounds are playing?
+ void stopSounds(); ///< Stops all sounds
+ void removeStoppedSounds(); ///< Removes all stopped sounds from the MIDI channels
+
+ // For playing Apple IIGS AGI samples (Sound effects etc)
+ bool playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample);
+ void playMidiSound();
+ void playSampleSound();
+
+ // MIDI commands
+ void midiNoteOff(uint8 channel, uint8 note, uint8 velocity);
+ void midiNoteOn(uint8 channel, uint8 note, uint8 velocity);
+ void midiController(uint8 channel, uint8 controller, uint8 value);
+ void midiProgramChange(uint8 channel, uint8 program);
+ void midiPitchWheel(uint8 wheelPos);
+ //protected:
+ const IIgsInstrumentHeader* getInstrument(uint8 program) const;
+ //public:
+ Common::Array<IIgsMidiChannel> _midiChannels; ///< Information about each MIDI channel
+ //protected:
+ Common::Array<int8> _wave; ///< Sample data used by the Apple IIGS MIDI instruments
+ const MidiProgramChangeMapping *_midiProgToInst; ///< MIDI program change to instrument number mapping
+ Common::Array<IIgsInstrumentHeader> _instruments; ///< Instruments used by the Apple IIGS AGI
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_2GS_H */
diff --git a/engines/agi/sound_coco3.cpp b/engines/agi/sound_coco3.cpp
new file mode 100644
index 0000000000..f054be0682
--- /dev/null
+++ b/engines/agi/sound_coco3.cpp
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "agi/agi.h"
+
+#include "agi/sound_coco3.h"
+
+namespace Agi {
+
+static int cocoFrequencies[] = {
+ 130, 138, 146, 155, 164, 174, 184, 195, 207, 220, 233, 246,
+ 261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493,
+ 523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987,
+ 1046, 1108, 1174, 1244, 1318, 1396, 1479, 1567, 1661, 1760, 1864, 1975,
+ 2093, 2217, 2349, 2489, 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951
+};
+
+SoundGenCoCo3::SoundGenCoCo3(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) {
+}
+
+SoundGenCoCo3::~SoundGenCoCo3() {
+}
+
+void SoundGenCoCo3::play(int resnum) {
+ int i = cocoFrequencies[0]; // Silence warning
+
+ i = i + 1;
+
+#if 0
+ int i = 0;
+ CoCoNote note;
+
+ do {
+ note.read(_chn[i].ptr);
+
+ if (note.freq != 0xff) {
+ playNote(0, cocoFrequencies[note.freq], note.volume);
+
+ uint32 start_time = _vm->_system->getMillis();
+
+ while (_vm->_system->getMillis() < start_time + note.duration) {
+ _vm->_system->updateScreen();
+
+ _vm->_system->delayMillis(10);
+ }
+ }
+ } while (note.freq != 0xff);
+#endif
+}
+
+void SoundGenCoCo3::stop() {
+}
+
+int SoundGenCoCo3::readBuffer(int16 *buffer, const int numSamples) {
+ return numSamples;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_coco3.h b/engines/agi/sound_coco3.h
new file mode 100644
index 0000000000..b60f1937cd
--- /dev/null
+++ b/engines/agi/sound_coco3.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 AGI_SOUND_COCO3_H
+#define AGI_SOUND_COCO3_H
+
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+struct CoCoNote {
+ uint8 freq;
+ uint8 volume;
+ uint16 duration; ///< Note duration
+
+ /** Reads a CoCoNote through the given pointer. */
+ void read(const uint8 *ptr) {
+ freq = *ptr;
+ volume = *(ptr + 1);
+ duration = READ_LE_UINT16(ptr + 2);
+ }
+};
+
+class SoundGenCoCo3 : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGenCoCo3(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenCoCo3();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_COCO3_H */
diff --git a/engines/agi/sound_midi.cpp b/engines/agi/sound_midi.cpp
new file mode 100644
index 0000000000..57c5d54b27
--- /dev/null
+++ b/engines/agi/sound_midi.cpp
@@ -0,0 +1,345 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Code is based on:
+//
+// A very simple program, that converts an AGI-song into a MIDI-song.
+// Feel free to use it for anything.
+//
+// The default instrument is "piano" for all the channels, what gives
+// good results on most games. But I found, that some songs are interesting
+// with other instruments. If you want to experiment, modify the "instr"
+// array.
+//
+// Timing is not perfect, yet. It plays correct, when I use the
+// Gravis-Midiplayer, but the songs are too fast when I use playmidi on
+// Linux.
+//
+// Original program developed by Jens. Christian Restemeier
+//
+
+// MIDI and digital music class
+
+#include "sound/audiostream.h"
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/stream.h"
+
+#include "agi/agi.h"
+
+#include "agi/sound.h"
+#include "agi/sound_midi.h"
+
+#define SPEED_FACTOR 6
+
+namespace Agi {
+
+static uint32 convertSND2MIDI(byte *snddata, byte **data);
+
+MIDISound::MIDISound(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ _data = data; // Save the resource pointer
+ _len = len; // Save the resource's length
+ _type = READ_LE_UINT16(data); // Read sound resource's type
+ _isValid = (_type == AGI_SOUND_4CHN) && (_data != NULL) && (_len >= 2);
+
+ if (!_isValid) // Check for errors
+ warning("Error creating MIDI sound from resource %d (Type %d, length %d)", resnum, _type, len);
+}
+
+SoundGenMIDI::SoundGenMIDI(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer), _parser(0), _isPlaying(false), _passThrough(false), _isGM(false) {
+ DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
+ _driver = MidiDriver::createMidi(dev);
+
+ memset(_channel, 0, sizeof(_channel));
+ memset(_channelVolume, 255, sizeof(_channelVolume));
+ _masterVolume = 0;
+ this->open();
+ _smfParser = MidiParser::createParser_SMF();
+ _midiMusicData = NULL;
+}
+
+SoundGenMIDI::~SoundGenMIDI() {
+ _driver->setTimerCallback(NULL, NULL);
+ stop();
+ this->close();
+ _smfParser->setMidiDriver(NULL);
+ delete _smfParser;
+ delete[] _midiMusicData;
+}
+
+void SoundGenMIDI::setChannelVolume(int channel) {
+ int newVolume = _channelVolume[channel] * _masterVolume / 255;
+ _channel[channel]->volume(newVolume);
+}
+
+void SoundGenMIDI::setVolume(int volume) {
+ Common::StackLock lock(_mutex);
+
+ volume = CLIP(volume, 0, 255);
+ if (_masterVolume == volume)
+ return;
+ _masterVolume = volume;
+
+ for (int i = 0; i < 16; ++i) {
+ if (_channel[i]) {
+ setChannelVolume(i);
+ }
+ }
+}
+
+int SoundGenMIDI::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 SoundGenMIDI::close() {
+ stop();
+ if (_driver)
+ _driver->close();
+ _driver = 0;
+}
+
+void SoundGenMIDI::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 a new channel is allocated during the playback, make sure
+ // its volume is correctly initialized.
+ if (_channel[channel])
+ setChannelVolume(channel);
+ }
+
+ if (_channel[channel])
+ _channel[channel]->send(b);
+}
+
+void SoundGenMIDI::metaEvent(byte type, byte *data, uint16 length) {
+
+ switch (type) {
+ case 0x2F: // End of Track
+ stop();
+ _vm->_sound->soundIsFinished();
+ break;
+ default:
+ //warning("Unhandled meta event: %02x", type);
+ break;
+ }
+}
+
+void SoundGenMIDI::onTimer(void *refCon) {
+ SoundGenMIDI *music = (SoundGenMIDI *)refCon;
+ Common::StackLock lock(music->_mutex);
+
+ if (music->_parser)
+ music->_parser->onTimer();
+}
+
+void SoundGenMIDI::play(int resnum) {
+ MIDISound *track;
+
+ stop();
+
+ _isGM = true;
+
+ track = (MIDISound *)_vm->_game.sounds[resnum];
+
+ // Convert AGI Sound data to MIDI
+ int midiMusicSize = convertSND2MIDI(track->_data, &_midiMusicData);
+
+ if (_smfParser->loadMusic(_midiMusicData, midiMusicSize)) {
+ MidiParser *parser = _smfParser;
+ parser->setTrack(0);
+ parser->setMidiDriver(this);
+ parser->setTimerRate(getBaseTempo());
+ parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+
+ _parser = parser;
+
+ syncVolume();
+
+ _isPlaying = true;
+ }
+}
+
+void SoundGenMIDI::stop() {
+ Common::StackLock lock(_mutex);
+
+ if (!_isPlaying)
+ return;
+
+ _isPlaying = false;
+ if (_parser) {
+ _parser->unloadMusic();
+ _parser = NULL;
+ }
+}
+
+void SoundGenMIDI::pause() {
+ setVolume(-1);
+ _isPlaying = false;
+}
+
+void SoundGenMIDI::resume() {
+ syncVolume();
+ _isPlaying = true;
+}
+
+void SoundGenMIDI::syncVolume() {
+ int volume = ConfMan.getInt("music_volume");
+ if (ConfMan.getBool("mute")) {
+ volume = -1;
+ }
+ setVolume(volume);
+}
+
+/* channel / intrument setup: */
+
+/* most songs are good with this: */
+unsigned char instr[] = {0, 0, 0};
+
+/* cool for sq2:
+unsigned char instr[] = {50, 51, 19};
+*/
+
+static void writeDelta(Common::MemoryWriteStreamDynamic *st, int32 delta) {
+ int32 i;
+
+ i = delta >> 21; if (i > 0) st->writeByte((i & 127) | 128);
+ i = delta >> 14; if (i > 0) st->writeByte((i & 127) | 128);
+ i = delta >> 7; if (i > 0) st->writeByte((i & 127) | 128);
+ st->writeByte(delta & 127);
+}
+
+static uint32 convertSND2MIDI(byte *snddata, byte **data) {
+ int32 lp, ep;
+ int n;
+ double ll;
+
+ Common::MemoryWriteStreamDynamic st;
+
+ ll = log10(pow(2.0, 1.0 / 12.0));
+
+ /* Header */
+ st.write("MThd", 4);
+ st.writeUint32BE(6);
+ st.writeUint16BE(1); /* mode */
+ st.writeUint16BE(3); /* number of tracks */
+ st.writeUint16BE(192); /* ticks / quarter */
+
+ for (n = 0; n < 3; n++) {
+ uint16 start, end, pos;
+
+ st.write("MTrk", 4);
+ lp = st.pos();
+ st.writeUint32BE(0); /* chunklength */
+ writeDelta(&st, 0); /* set instrument */
+ st.writeByte(0xc0 + n);
+ st.writeByte(instr[n]);
+ start = snddata[n * 2 + 0] | (snddata[n * 2 + 1] << 8);
+ end = ((snddata[n * 2 + 2] | (snddata[n * 2 + 3] << 8))) - 5;
+
+ for (pos = start; pos < end; pos += 5) {
+ uint16 freq, dur;
+ dur = (snddata[pos + 0] | (snddata[pos + 1] << 8)) * SPEED_FACTOR;
+ freq = ((snddata[pos + 2] & 0x3F) << 4) + (snddata[pos + 3] & 0x0F);
+ if (snddata[pos + 2] > 0) {
+ double fr;
+ int note;
+ /* I don't know, what frequency equals midi note 0 ... */
+ /* This moves the song 4 octaves down: */
+ fr = (log10(111860.0 / (double)freq) / ll) - 48;
+ note = (int)floor(fr + 0.5);
+ if (note < 0) note = 0;
+ if (note > 127) note = 127;
+ /* note on */
+ writeDelta(&st, 0);
+ st.writeByte(144 + n);
+ st.writeByte(note);
+ st.writeByte(100);
+ /* note off */
+ writeDelta(&st, dur);
+ st.writeByte(128 + n);
+ st.writeByte(note);
+ st.writeByte(0);
+ } else {
+ /* note on */
+ writeDelta(&st, 0);
+ st.writeByte(144 + n);
+ st.writeByte(0);
+ st.writeByte(0);
+ /* note off */
+ writeDelta(&st, dur);
+ st.writeByte(128 + n);
+ st.writeByte(0);
+ st.writeByte(0);
+ }
+ }
+ writeDelta(&st, 0);
+ st.writeByte(0xff);
+ st.writeByte(0x2f);
+ st.writeByte(0x0);
+ ep = st.pos();
+ st.seek(lp, SEEK_SET);
+ st.writeUint32BE((ep - lp) - 4);
+ st.seek(ep, SEEK_SET);
+ }
+
+ *data = st.getData();
+
+ return st.pos();
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_midi.h b/engines/agi/sound_midi.h
new file mode 100644
index 0000000000..26b75e0d70
--- /dev/null
+++ b/engines/agi/sound_midi.h
@@ -0,0 +1,114 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 AGI_SOUND_MIDI_H
+#define AGI_SOUND_MIDI_H
+
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "common/mutex.h"
+
+namespace Agi {
+
+class MIDISound : public AgiSound {
+public:
+ MIDISound(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~MIDISound() { free(_data); }
+ virtual uint16 type() { return _type; }
+ uint8 *_data; ///< Raw sound resource data
+ uint32 _len; ///< Length of the raw sound resource
+
+protected:
+ uint16 _type; ///< Sound resource type
+};
+
+class SoundGenMIDI : public SoundGen, public MidiDriver {
+public:
+ SoundGenMIDI(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenMIDI();
+
+ void play(int resnum);
+ void stop();
+
+ bool isPlaying() { return _isPlaying; }
+ void setPlaying(bool playing) { _isPlaying = playing; }
+
+ void setVolume(int volume);
+ int getVolume() { return _masterVolume; }
+ void syncVolume();
+
+ void setNativeMT32(bool b) { _nativeMT32 = b; }
+ bool hasNativeMT32() { return _nativeMT32; }
+ void pause();
+ void resume();
+ 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() { return _driver ? _driver->getBaseTempo() : 0; }
+
+ // Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+ MidiParser *_parser;
+ Common::Mutex _mutex;
+
+private:
+
+ static void onTimer(void *data);
+ void setChannelVolume(int channel);
+
+ MidiChannel *_channel[16];
+ MidiDriver *_driver;
+ MidiParser *_smfParser;
+ byte _channelVolume[16];
+ bool _nativeMT32;
+ bool _isGM;
+ bool _passThrough;
+
+ bool _isPlaying;
+ bool _looping;
+ byte _masterVolume;
+
+ byte *_midiMusicData;
+
+ SoundMgr *_manager;
+};
+
+} // End of namespace Agi
+
+#endif
diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp
new file mode 100644
index 0000000000..b9d701d7f7
--- /dev/null
+++ b/engines/agi/sound_pcjr.cpp
@@ -0,0 +1,512 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/* Heavily based on code from NAGI
+ *
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright (c) 2001, 2001, 2002 Nick Sonneveld
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, and/or sell copies of the Software, and to permit persons
+ * to whom the Software is furnished to do so, provided that the above
+ * copyright notice(s) and this permission notice appear in all copies of
+ * the Software and that both the above copyright notice(s) and this
+ * permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+ * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale, use
+ * or other dealings in this Software without prior written authorization
+ *
+ */
+
+#include "agi/agi.h"
+#include "agi/sound.h"
+#include "agi/sound_pcjr.h"
+
+namespace Agi {
+
+// "fade out" or possibly "dissolve"
+// v2.9xx
+const int8 dissolveDataV2[] = {
+ -2, -3, -2, -1,
+ 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09,
+ 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0D,
+ -100
+};
+
+// v3
+const int8 dissolveDataV3[] = {
+ -2, -3, -2, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09,
+ 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0D,
+ -100
+};
+
+
+SoundGenPCJr::SoundGenPCJr(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) {
+ _chanAllocated = 10240; // preallocate something which will most likely fit
+ _chanData = (int16 *)malloc(_chanAllocated << 1);
+
+ // Pick dissolve method
+ //
+ // 0 = no dissolve.. just play for as long as it's meant to be played
+ // this was used in older v2.4 and under games i THINK
+ // 1 = not used
+ // 2 = v2.9+ games used a shorter dissolve
+ // 3 (default) = v3 games used this dissolve pattern.. slightly longer
+ if (_vm->getVersion() >= 0x3000)
+ _dissolveMethod = 3;
+ else if (_vm->getVersion() >= 0x2900)
+ _dissolveMethod = 2;
+ else
+ _dissolveMethod = 0;
+
+ _dissolveMethod = 3;
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundGenPCJr::~SoundGenPCJr() {
+ free(_chanData);
+
+ _mixer->stopHandle(_soundHandle);
+}
+
+void SoundGenPCJr::play(int resnum) {
+ PCjrSound *pcjrSound = (PCjrSound *)_vm->_game.sounds[resnum];
+
+ for (int i = 0; i < CHAN_MAX; i++) {
+ _channel[i].data = pcjrSound->getVoicePointer(i % 4);
+ _channel[i].duration = 0;
+ _channel[i].avail = 0xffff;
+ _channel[i].dissolveCount = 0xFFFF;
+ _channel[i].attenuation = 0;
+ _channel[i].attenuationCopy = 0;
+
+ _tchannel[i].avail = 1;
+ _tchannel[i].noteCount = 0;
+ _tchannel[i].freqCount = 250;
+ _tchannel[i].freqCountPrev = -1;
+ _tchannel[i].atten = 0xF; // silence
+ _tchannel[i].genType = kGenTone;
+ _tchannel[i].genTypePrev = -1;
+ }
+}
+
+void SoundGenPCJr::stop(void) {
+ int i;
+
+ for (i = 0; i < CHAN_MAX ; i++) {
+ _channel[i].avail = 0;
+ _tchannel[i].avail = 0;
+ }
+}
+
+int SoundGenPCJr::volumeCalc(SndGenChan *chan) {
+ int8 attenuation, dissolveValue;
+
+ const int8 *dissolveData;
+
+ switch (_dissolveMethod) {
+ case 2:
+ dissolveData = dissolveDataV2;
+ break;
+ case 3:
+ default:
+ dissolveData = dissolveDataV3;
+ break;
+ }
+
+ assert(chan);
+
+ attenuation = chan->attenuation;
+ if (attenuation != 0x0F) { // != silence
+ if (chan->dissolveCount != 0xFFFF) {
+ dissolveValue = dissolveData[chan->dissolveCount];
+ if (dissolveValue == -100) { // if at end of list
+ chan->dissolveCount = 0xFFFF;
+ chan->attenuation = chan->attenuationCopy;
+ attenuation = chan->attenuation;
+ } else {
+ chan->dissolveCount++;
+
+ attenuation += dissolveValue;
+ if (attenuation < 0)
+ attenuation = 0;
+ if (attenuation > 0x0F)
+ attenuation = 0x0F;
+
+ chan->attenuationCopy = attenuation;
+
+ attenuation &= 0x0F;
+ attenuation += _vm->getvar(vVolume);
+ if (attenuation > 0x0F)
+ attenuation = 0x0F;
+ }
+ }
+ //if (computer_type == 2) && (attenuation < 8)
+ if (attenuation < 8)
+ attenuation += 2;
+ }
+
+ return attenuation;
+}
+
+// read the next channel data.. fill it in *tone
+// if tone isn't touched.. it should be inited so it just plays silence
+// return 0 if it's passing more data
+// return -1 if it's passing nothing (end of data)
+int SoundGenPCJr::getNextNote(int ch, Tone *tone) {
+ SndGenChan *chan;
+ const byte *data;
+
+ assert(tone);
+ assert(ch < CHAN_MAX);
+
+ if (!_vm->getflag(fSoundOn))
+ return -1;
+
+ chan = &_channel[ch];
+ if (!chan->avail)
+ return -1;
+
+ while ((chan->duration == 0) && (chan->duration != 0xFFFF)) {
+ data = chan->data;
+
+ // read the duration of the note
+ chan->duration = READ_LE_UINT16(data); // duration
+
+ // if it's 0 then it's not going to be played
+ // if it's 0xFFFF then the channel data has finished.
+ if ((chan->duration != 0) && (chan->duration != 0xFFFF)) {
+ // only tone channels dissolve
+ if ((ch != 3) && (_dissolveMethod != 0)) // != noise??
+ chan->dissolveCount = 0;
+
+ // attenuation (volume)
+ chan->attenuation = data[4] & 0xF;
+
+ // frequency
+ if (ch < (CHAN_MAX - 1)) {
+ chan->freqCount = (uint16)data[2] & 0x3F;
+ chan->freqCount <<= 4;
+ chan->freqCount |= data[3] & 0x0F;
+
+ chan->genType = kGenTone;
+ } else {
+ int noiseFreq;
+
+ // check for white noise (1) or periodic (0)
+ chan->genType = (data[3] & 0x04) ? kGenWhite : kGenPeriod;
+
+ noiseFreq = data[3] & 0x03;
+
+ switch (noiseFreq) {
+ case 0:
+ chan->freqCount = 32;
+ break;
+ case 1:
+ chan->freqCount = 64;
+ break;
+ case 2:
+ chan->freqCount = 128;
+ break;
+ case 3:
+ chan->freqCount = _channel[2].freqCount * 2;
+ break;
+ }
+ }
+ }
+ // data now points to the next data seg-a-ment
+ chan->data += 5;
+ }
+
+ if (chan->duration != 0xFFFF) {
+ tone->freqCount = chan->freqCount;
+ tone->atten = volumeCalc(chan); // calc volume, sent vol is different from saved vol
+ tone->type = chan->genType;
+ chan->duration--;
+ } else {
+ // kill channel
+ chan->avail = 0;
+ chan->attenuation = 0x0F; // silent
+ chan->attenuationCopy = 0x0F; // dunno really
+
+ return -1;
+ }
+
+ return 0;
+}
+
+// Formulas for noise generator
+// bit0 = output
+
+// noise feedback for white noise mode
+#define FB_WNOISE 0x12000 // bit15.d(16bits) = bit0(out) ^ bit2
+//#define FB_WNOISE 0x14000 // bit15.d(16bits) = bit0(out) ^ bit1
+//#define FB_WNOISE 0x28000 // bit16.d(17bits) = bit0(out) ^ bit2 (same to AY-3-8910)
+//#define FB_WNOISE 0x50000 // bit17.d(18bits) = bit0(out) ^ bit2
+
+// noise feedback for periodic noise mode
+// it is correct maybe (it was in the Megadrive sound manual)
+//#define FB_PNOISE 0x10000 // 16bit rorate
+#define FB_PNOISE 0x08000
+
+// noise generator start preset (for periodic noise)
+#define NG_PRESET 0x0f35
+
+//#define WAVE_HEIGHT (0x7FFF)
+
+// Volume table.
+//
+// 2dB = 20*log(a/b)
+// 10^(2/20)*b = a;
+// value = 0x7fff;
+// value /= 1.258925411794;
+const int16 volTable[16] = {
+ 32767, 26027, 20674, 16422, 13044, 10361, 8230, 6537, 5193, 4125, 3276, 2602, 2067, 1642, 1304, 0
+};
+
+#define FREQ_DIV 111844
+#define MULT FREQ_DIV
+
+// fill buff
+int SoundGenPCJr::chanGen(int chan, int16 *stream, int len) {
+ ToneChan *tpcm;
+ Tone toneNew;
+ int fillSize;
+ int retVal;
+
+ tpcm = &_tchannel[chan];
+
+ retVal = -1;
+
+ while (len > 0) {
+ if (tpcm->noteCount <= 0) {
+ // get new tone data
+ toneNew.freqCount = 0;
+ toneNew.atten = 0xF;
+ toneNew.type = kGenTone;
+ if ((tpcm->avail) && (getNextNote(chan, &toneNew) == 0)) {
+ tpcm->atten = toneNew.atten;
+ tpcm->freqCount = toneNew.freqCount;
+ tpcm->genType = toneNew.type;
+
+ // setup counters 'n stuff
+ // SAMPLE_RATE samples per sec.. tone changes 60 times per sec
+ tpcm->noteCount = SAMPLE_RATE / 60;
+ retVal = 0;
+ } else {
+ // if it doesn't return an
+ tpcm->genType = kGenSilence;
+ tpcm->noteCount = len;
+ tpcm->avail = 0;
+ }
+ }
+
+ // write nothing
+ if ((tpcm->freqCount == 0) || (tpcm->atten == 0xf)) {
+ tpcm->genType = kGenSilence;
+ }
+
+ // find which is smaller.. the buffer or the
+ fillSize = (tpcm->noteCount <= len) ? tpcm->noteCount : len;
+
+ switch (tpcm->genType) {
+ case kGenTone:
+ fillSize = fillSquare(tpcm, stream, fillSize);
+ break;
+ case kGenPeriod:
+ case kGenWhite:
+ fillSize = fillNoise(tpcm, stream, fillSize);
+ break;
+ case kGenSilence:
+ default:
+ // fill with whitespace
+ memset(stream, 0, fillSize * sizeof(int16));
+ break;
+ }
+
+ tpcm->noteCount -= fillSize;
+ stream += fillSize;
+ len -= fillSize;
+ }
+
+ return retVal;
+}
+
+int SoundGenPCJr::fillSquare(ToneChan *t, int16 *buf, int len) {
+ int count;
+
+ if (t->genType != t->genTypePrev) {
+ // make sure the freqCount is checked
+ t->freqCountPrev = -1;
+ t->sign = 1;
+ t->genTypePrev = t->genType;
+ }
+
+ if (t->freqCount != t->freqCountPrev) {
+ //t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5);
+ t->scale = (SAMPLE_RATE / 2) * t->freqCount;
+ t->count = t->scale;
+ t->freqCountPrev = t->freqCount;
+ }
+
+ count = len;
+
+ while (count > 0) {
+ *(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten];
+ count--;
+
+ // get next sample
+ t->count -= MULT;
+ while (t->count <= 0) {
+ t->sign ^= 1;
+ t->count += t->scale;
+ }
+ }
+
+ return len;
+}
+
+int SoundGenPCJr::fillNoise(ToneChan *t, int16 *buf, int len) {
+ int count;
+
+ if (t->genType != t->genTypePrev) {
+ // make sure the freqCount is checked
+ t->freqCountPrev = -1;
+ t->genTypePrev = t->genType;
+ }
+
+ if (t->freqCount != t->freqCountPrev) {
+ //t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5);
+ t->scale = (SAMPLE_RATE / 2) * t->freqCount;
+ t->count = t->scale;
+ t->freqCountPrev = t->freqCount;
+
+ t->feedback = (t->genType == kGenWhite) ? FB_WNOISE : FB_PNOISE;
+ // reset noise shifter
+ t->noiseState = NG_PRESET;
+ t->sign = t->noiseState & 1;
+ }
+
+ count = len;
+
+ while (count > 0) {
+ *(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten];
+ count--;
+
+ // get next sample
+ t->count -= MULT;
+ while (t->count <= 0) {
+ if (t->noiseState & 1)
+ t->noiseState ^= t->feedback;
+
+ t->noiseState >>= 1;
+ t->sign = t->noiseState & 1;
+ t->count += t->scale;
+ }
+ }
+
+ return len;
+}
+
+int SoundGenPCJr::readBuffer(int16 *stream, const int len) {
+ int streamCount;
+ int16 *sPtr, *cPtr;
+
+ if (_chanAllocated < len) {
+ free(_chanData);
+ _chanData = (int16 *)malloc(len << 1);
+ _chanAllocated = len;
+ }
+ memset(stream, 0, len << 1);
+
+ assert(stream);
+
+ bool finished = true;
+
+ for (int i = 0; i < CHAN_MAX; i++) {
+ // get channel data(chan.userdata)
+ if (chanGen(i, _chanData, len) == 0) {
+ // divide by number of channels then add to stream
+ streamCount = len;
+ sPtr = stream;
+ cPtr = _chanData;
+
+ while (streamCount--)
+ *(sPtr++) += *(cPtr++) / CHAN_MAX;
+
+ finished = false;
+ }
+ }
+
+ if (finished)
+ _vm->_sound->soundIsFinished();
+
+ return len;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_pcjr.h b/engines/agi/sound_pcjr.h
new file mode 100644
index 0000000000..fe0e762f4e
--- /dev/null
+++ b/engines/agi/sound_pcjr.h
@@ -0,0 +1,127 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 AGI_SOUND_PCJR_H
+#define AGI_SOUND_PCJR_H
+
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+#define CHAN_MAX 4
+
+#define SAMPLE_RATE 22050
+
+enum GenType {
+ kGenSilence,
+ kGenTone,
+ kGenPeriod,
+ kGenWhite
+};
+
+struct SndGenChan {
+ const byte *data;
+ uint16 duration;
+ uint16 avail; // turned on (1) but when the channel's data runs out, it's set to (0)
+ uint16 dissolveCount;
+ byte attenuation;
+ byte attenuationCopy;
+
+ GenType genType;
+
+ // for the sample mixer
+ int freqCount;
+};
+
+struct ToneChan {
+ int avail;
+
+ int noteCount; // length of tone.. duration
+
+ int freqCount;
+ int freqCountPrev;
+ int atten; // volume
+
+ GenType genType;
+ int genTypePrev;
+
+ int count;
+ int scale;
+ int sign;
+ unsigned int noiseState; /* noise generator */
+ int feedback; /* noise feedback mask */
+};
+
+struct Tone {
+ int freqCount;
+ int atten;
+ GenType type;
+};
+
+class SoundGenPCJr : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGenPCJr(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenPCJr();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+
+private:
+ int getNextNote(int ch, Tone *tone);
+ int volumeCalc(SndGenChan *chan);
+
+ int chanGen(int chan, int16 *stream, int len);
+
+ int fillNoise(ToneChan *t, int16 *buf, int len);
+ int fillSquare(ToneChan *t, int16 *buf, int len);
+
+private:
+ SndGenChan _channel[CHAN_MAX];
+ ToneChan _tchannel[CHAN_MAX];
+ int16 *_chanData;
+ int _chanAllocated;
+
+ int _dissolveMethod;
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_PCJR_H */
diff --git a/engines/agi/sound_sarien.cpp b/engines/agi/sound_sarien.cpp
new file mode 100644
index 0000000000..08bdd47497
--- /dev/null
+++ b/engines/agi/sound_sarien.cpp
@@ -0,0 +1,357 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/md5.h"
+#include "common/config-manager.h"
+#include "common/fs.h"
+#include "common/random.h"
+#include "common/str-array.h"
+
+#include "sound/mididrv.h"
+
+#include "agi/agi.h"
+
+#include "agi/sound_sarien.h"
+
+namespace Agi {
+
+#define USE_INTERPOLATION
+
+static const int16 waveformRamp[WAVEFORM_SIZE] = {
+ 0, 8, 16, 24, 32, 40, 48, 56,
+ 64, 72, 80, 88, 96, 104, 112, 120,
+ 128, 136, 144, 152, 160, 168, 176, 184,
+ 192, 200, 208, 216, 224, 232, 240, 255,
+ 0, -248, -240, -232, -224, -216, -208, -200,
+ -192, -184, -176, -168, -160, -152, -144, -136,
+ -128, -120, -112, -104, -96, -88, -80, -72,
+ -64, -56, -48, -40, -32, -24, -16, -8 // Ramp up
+};
+
+static const int16 waveformSquare[WAVEFORM_SIZE] = {
+ 255, 230, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 110,
+ -255, -230, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -110, 0, 0, 0, 0 // Square
+};
+
+static const int16 waveformMac[WAVEFORM_SIZE] = {
+ 45, 110, 135, 161, 167, 173, 175, 176,
+ 156, 137, 123, 110, 91, 72, 35, -2,
+ -60, -118, -142, -165, -170, -176, -177, -179,
+ -177, -176, -164, -152, -117, -82, -17, 47,
+ 92, 137, 151, 166, 170, 173, 171, 169,
+ 151, 133, 116, 100, 72, 43, -7, -57,
+ -99, -141, -156, -170, -174, -177, -178, -179,
+ -175, -172, -165, -159, -137, -114, -67, -19
+};
+
+SoundGenSarien::SoundGenSarien(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer), _chn() {
+ _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+ _env = false;
+ _playingSound = -1;
+ _playing = false;
+ _useChorus = true; // FIXME: Currently always true?
+
+ switch (_vm->_soundemu) {
+ case SOUND_EMU_NONE:
+ _waveform = waveformRamp;
+ _env = true;
+ break;
+ case SOUND_EMU_AMIGA:
+ case SOUND_EMU_PC:
+ _waveform = waveformSquare;
+ break;
+ case SOUND_EMU_MAC:
+ _waveform = waveformMac;
+ break;
+ }
+
+ report("Initializing sound:\n");
+
+ report("sound: envelopes ");
+ if (_env) {
+ report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
+ } else {
+ report("disabled\n");
+ }
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundGenSarien::~SoundGenSarien() {
+ _mixer->stopHandle(_soundHandle);
+
+ free(_sndBuffer);
+}
+
+int SoundGenSarien::readBuffer(int16 *buffer, const int numSamples) {
+ fillAudio(buffer, numSamples / 2);
+
+ return numSamples;
+}
+
+void SoundGenSarien::play(int resnum) {
+ AgiSoundEmuType type;
+
+ type = (AgiSoundEmuType)_vm->_game.sounds[resnum]->type();
+
+ assert(type == AGI_SOUND_4CHN);
+
+ _playingSound = resnum;
+
+ PCjrSound *pcjrSound = (PCjrSound *) _vm->_game.sounds[resnum];
+
+ // Initialize channel info
+ for (int i = 0; i < NUM_CHANNELS; i++) {
+ _chn[i].type = type;
+ _chn[i].flags = AGI_SOUND_LOOP;
+
+ if (_env) {
+ _chn[i].flags |= AGI_SOUND_ENVELOPE;
+ _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
+ }
+
+ _chn[i].ins = _waveform;
+ _chn[i].size = WAVEFORM_SIZE;
+ _chn[i].ptr = pcjrSound->getVoicePointer(i % 4);
+ _chn[i].timer = 0;
+ _chn[i].vol = 0;
+ _chn[i].end = 0;
+ }
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+}
+
+void SoundGenSarien::stop() {
+ _playingSound = -1;
+
+ for (int i = 0; i < NUM_CHANNELS; i++)
+ stopNote(i);
+}
+
+void SoundGenSarien::stopNote(int i) {
+ _chn[i].adsr = AGI_SOUND_ENV_RELEASE;
+
+ if (_useChorus) {
+ // Stop chorus ;)
+ if (_chn[i].type == AGI_SOUND_4CHN &&
+ _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
+ stopNote(i + 4);
+ }
+ }
+}
+
+void SoundGenSarien::playNote(int i, int freq, int vol) {
+ if (!_vm->getflag(fSoundOn))
+ vol = 0;
+ else if (vol && _vm->_soundemu == SOUND_EMU_PC)
+ vol = 160;
+
+ _chn[i].phase = 0;
+ _chn[i].freq = freq;
+ _chn[i].vol = vol;
+ _chn[i].env = 0x10000;
+ _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
+
+ if (_useChorus) {
+ // Add chorus ;)
+ if (_chn[i].type == AGI_SOUND_4CHN &&
+ _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
+
+ int newfreq = freq * 1007 / 1000;
+
+ if (freq == newfreq)
+ newfreq++;
+
+ playNote(i + 4, newfreq, vol * 2 / 3);
+ }
+ }
+}
+
+void SoundGenSarien::playSound() {
+ int i;
+ AgiNote note;
+
+ if (_playingSound == -1)
+ return;
+
+ _playing = false;
+ for (i = 0; i < (_vm->_soundemu == SOUND_EMU_PC ? 1 : 4); i++) {
+ _playing |= !_chn[i].end;
+ note.read(_chn[i].ptr); // Read a single note (Doesn't advance the pointer)
+
+ if (_chn[i].end)
+ continue;
+
+ if ((--_chn[i].timer) <= 0) {
+ stopNote(i);
+
+ if (note.freqDiv != 0) {
+ int volume = (note.attenuation == 0x0F) ? 0 : (0xFF - note.attenuation * 2);
+ playNote(i, note.freqDiv * 10, volume);
+ }
+
+ _chn[i].timer = note.duration;
+
+ if (_chn[i].timer == 0xffff) {
+ _chn[i].end = 1;
+ _chn[i].vol = 0;
+ _chn[i].env = 0;
+
+ if (_useChorus) {
+ // chorus
+ if (_chn[i].type == AGI_SOUND_4CHN && _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
+ _chn[i + 4].vol = 0;
+ _chn[i + 4].env = 0;
+ }
+ }
+ }
+ _chn[i].ptr += 5; // Advance the pointer to the next note data (5 bytes per note)
+ }
+ }
+
+ if (!_playing) {
+ _vm->_sound->soundIsFinished();
+
+ _playingSound = -1;
+ }
+}
+
+uint32 SoundGenSarien::mixSound() {
+ register int i, p;
+ const int16 *src;
+ int c, b, m;
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+
+ if (!_playing || _playingSound == -1)
+ return BUFFER_SIZE;
+
+ // Handle PCjr 4-channel sound mixing here
+ for (c = 0; c < NUM_CHANNELS; c++) {
+ if (!_chn[c].vol)
+ continue;
+
+ m = _chn[c].flags & AGI_SOUND_ENVELOPE ?
+ _chn[c].vol * _chn[c].env >> 16 : _chn[c].vol;
+
+ if (_chn[c].type != AGI_SOUND_4CHN || c != 3) {
+ src = _chn[c].ins;
+
+ p = _chn[c].phase;
+ for (i = 0; i < BUFFER_SIZE; i++) {
+ b = src[p >> 8];
+#ifdef USE_INTERPOLATION
+ b += ((src[((p >> 8) + 1) % _chn[c].size] - src[p >> 8]) * (p & 0xff)) >> 8;
+#endif
+ _sndBuffer[i] += (b * m) >> 4;
+
+ p += (uint32) 118600 *4 / _chn[c].freq;
+
+ // FIXME: Fingolfin asks: why is there a FIXME here? Please either clarify what
+ // needs fixing, or remove it!
+ // FIXME
+ if (_chn[c].flags & AGI_SOUND_LOOP) {
+ p %= _chn[c].size << 8;
+ } else {
+ if (p >= _chn[c].size << 8) {
+ p = _chn[c].vol = 0;
+ _chn[c].end = 1;
+ break;
+ }
+ }
+
+ }
+ _chn[c].phase = p;
+ } else {
+ // Add white noise
+ for (i = 0; i < BUFFER_SIZE; i++) {
+ b = _vm->_rnd->getRandomNumber(255) - 128;
+ _sndBuffer[i] += (b * m) >> 4;
+ }
+ }
+
+ switch (_chn[c].adsr) {
+ case AGI_SOUND_ENV_ATTACK:
+ // not implemented
+ _chn[c].adsr = AGI_SOUND_ENV_DECAY;
+ break;
+ case AGI_SOUND_ENV_DECAY:
+ if (_chn[c].env > _chn[c].vol * ENV_SUSTAIN + ENV_DECAY) {
+ _chn[c].env -= ENV_DECAY;
+ } else {
+ _chn[c].env = _chn[c].vol * ENV_SUSTAIN;
+ _chn[c].adsr = AGI_SOUND_ENV_SUSTAIN;
+ }
+ break;
+ case AGI_SOUND_ENV_SUSTAIN:
+ break;
+ case AGI_SOUND_ENV_RELEASE:
+ if (_chn[c].env >= ENV_RELEASE) {
+ _chn[c].env -= ENV_RELEASE;
+ } else {
+ _chn[c].env = 0;
+ }
+ }
+ }
+
+ return BUFFER_SIZE;
+}
+
+void SoundGenSarien::fillAudio(int16 *stream, uint len) {
+ uint32 p = 0;
+
+ // current number of audio bytes in _sndBuffer
+ static uint32 data_available = 0;
+ // offset of start of audio bytes in _sndBuffer
+ static uint32 data_offset = 0;
+
+ len <<= 2;
+
+ debugC(5, kDebugLevelSound, "(%p, %d)", (void *)stream, len);
+
+ while (len > data_available) {
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, data_available);
+ p += data_available;
+ len -= data_available;
+
+ playSound();
+ data_available = mixSound() << 1;
+ data_offset = 0;
+ }
+
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, len);
+ data_offset += len;
+ data_available -= len;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_sarien.h b/engines/agi/sound_sarien.h
new file mode 100644
index 0000000000..54222ba624
--- /dev/null
+++ b/engines/agi/sound_sarien.h
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 AGI_SOUND_SARIEN_H
+#define AGI_SOUND_SARIEN_H
+
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+#define BUFFER_SIZE 410
+
+#define WAVEFORM_SIZE 64
+#define ENV_ATTACK 10000 /**< envelope attack rate */
+#define ENV_DECAY 1000 /**< envelope decay rate */
+#define ENV_SUSTAIN 100 /**< envelope sustain level */
+#define ENV_RELEASE 7500 /**< envelope release rate */
+#define NUM_CHANNELS 7 /**< number of sound channels */
+
+enum AgiSoundFlags {
+ AGI_SOUND_LOOP = 0x0001,
+ AGI_SOUND_ENVELOPE = 0x0002
+};
+enum AgiSoundEnv {
+ AGI_SOUND_ENV_ATTACK = 3,
+ AGI_SOUND_ENV_DECAY = 2,
+ AGI_SOUND_ENV_SUSTAIN = 1,
+ AGI_SOUND_ENV_RELEASE = 0
+};
+
+
+/**
+ * AGI engine sound channel structure.
+ */
+struct ChannelInfo {
+ AgiSoundEmuType type;
+ const uint8 *ptr; // Pointer to the AgiNote data
+ const int16 *ins;
+ int32 size;
+ uint32 phase;
+ uint32 flags; // ORs values from AgiSoundFlags
+ AgiSoundEnv adsr;
+ int32 timer;
+ uint32 end;
+ uint32 freq;
+ uint32 vol;
+ uint32 env;
+};
+
+class SoundGenSarien : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGenSarien(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenSarien();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+
+private:
+ ChannelInfo _chn[NUM_CHANNELS];
+ uint8 _env;
+
+ int16 *_sndBuffer;
+ const int16 *_waveform;
+
+ bool _useChorus;
+
+ bool _playing;
+ int _playingSound;
+
+private:
+ void playSound();
+ uint32 mixSound();
+ void fillAudio(int16 *stream, uint len);
+
+ void stopNote(int i);
+ void playNote(int i, int freq, int vol);
+
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_SARIEN_H */
diff --git a/engines/agi/sprite.cpp b/engines/agi/sprite.cpp
index 63ac880267..569481d772 100644
--- a/engines/agi/sprite.cpp
+++ b/engines/agi/sprite.cpp
@@ -241,6 +241,14 @@ void SpritesMgr::objsRestoreArea(Sprite *s) {
q += xSize;
pos0 += _WIDTH;
}
+
+ // WORKAROUND (see ScummVM bug #1945716)
+ // When set.view command is called, current code cannot detect this situation while updating
+ // Thus we force removal of the old sprite
+ if (s->v && s->v->viewReplaced) {
+ commitBlock(xPos, yPos, xPos + xSize, yPos + ySize);
+ s->v->viewReplaced = false;
+ }
}
@@ -332,6 +340,8 @@ void SpritesMgr::buildList(SpriteList &l, bool (*test)(VtEntry *, AgiEngine *))
}
}
+ debugC(5, kDebugLevelSprites, "buildList() --> entries %d", i);
+
// now look for the smallest y value in the array and put that
// sprite in the list
for (j = 0; j < i; j++) {
@@ -381,38 +391,20 @@ void SpritesMgr::freeList(SpriteList &l) {
* Copy sprites from the pic buffer to the screen buffer, and check if
* sprites of the given list have moved.
*/
-void SpritesMgr::commitSprites(SpriteList &l) {
+void SpritesMgr::commitSprites(SpriteList &l, bool immediate) {
SpriteList::iterator iter;
for (iter = l.begin(); iter != l.end(); ++iter) {
Sprite *s = *iter;
- int x1, y1, x2, y2, w, h;
+ int x1, y1, x2, y2;
- w = (s->v->celData->width > s->v->celData2->width) ?
- s->v->celData->width : s->v->celData2->width;
-
- h = (s->v->celData->height >
- s->v->celData2->height) ? s->v->celData->
- height : s->v->celData2->height;
+ x1 = MIN((int)MIN(s->v->xPos, s->v->xPos2), MIN(s->v->xPos + s->v->celData->width, s->v->xPos2 + s->v->celData2->width));
+ x2 = MAX((int)MAX(s->v->xPos, s->v->xPos2), MAX(s->v->xPos + s->v->celData->width, s->v->xPos2 + s->v->celData2->width));
+ y1 = MIN((int)MIN(s->v->yPos, s->v->yPos2), MIN(s->v->yPos - s->v->celData->height, s->v->yPos2 - s->v->celData2->height));
+ y2 = MAX((int)MAX(s->v->yPos, s->v->yPos2), MAX(s->v->yPos - s->v->celData->height, s->v->yPos2 - s->v->celData2->height));
s->v->celData2 = s->v->celData;
- if (s->v->xPos < s->v->xPos2) {
- x1 = s->v->xPos;
- x2 = s->v->xPos2 + w - 1;
- } else {
- x1 = s->v->xPos2;
- x2 = s->v->xPos + w - 1;
- }
-
- if (s->v->yPos < s->v->yPos2) {
- y1 = s->v->yPos - h + 1;
- y2 = s->v->yPos2;
- } else {
- y1 = s->v->yPos2 - h + 1;
- y2 = s->v->yPos;
- }
-
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, immediate);
if (s->v->stepTimeCount != s->v->stepTime)
continue;
@@ -452,7 +444,7 @@ void SpritesMgr::blitSprites(SpriteList& l) {
Sprite *s = *iter;
objsSaveArea(s);
- debugC(8, kDebugLevelSprites, "s->v->entry = %d (prio %d)", s->v->entry, s->v->priority);
+ debugC(8, kDebugLevelSprites, "blitSprites(): s->v->entry = %d (prio %d)", s->v->entry, s->v->priority);
hidden = blitCel(s->xPos, s->yPos, s->v->priority, s->v->celData, s->v->viewData->agi256_2);
if (s->v->entry == 0) { // if ego, update f1
@@ -528,7 +520,7 @@ void SpritesMgr::eraseBoth() {
* @see blit_both()
*/
void SpritesMgr::blitUpdSprites() {
- debugC(7, kDebugLevelSprites, "blit updating");
+ debugC(7, kDebugLevelSprites, "blitUpdSprites()");
buildUpdBlitlist();
blitSprites(_sprUpd);
}
@@ -542,7 +534,7 @@ void SpritesMgr::blitUpdSprites() {
* @see blit_both()
*/
void SpritesMgr::blitNonupdSprites() {
- debugC(7, kDebugLevelSprites, "blit non-updating");
+ debugC(7, kDebugLevelSprites, "blitNonupdSprites()");
buildNonupdBlitlist();
blitSprites(_sprNonupd);
}
@@ -578,7 +570,7 @@ void SpritesMgr::addToPic(int view, int loop, int cel, int x, int y, int pri, in
int x1, y1, x2, y2, y3;
uint8 *p1, *p2;
- debugC(3, kDebugLevelSprites, "v=%d, l=%d, c=%d, x=%d, y=%d, p=%d, m=%d", view, loop, cel, x, y, pri, mar);
+ debugC(3, kDebugLevelSprites, "addToPic(view=%d, loop=%d, cel=%d, x=%d, y=%d, pri=%d, mar=%d)", view, loop, cel, x, y, pri, mar);
_vm->recordImageStackCall(ADD_VIEW, view, loop, cel, x, y, pri, mar);
@@ -609,7 +601,7 @@ void SpritesMgr::addToPic(int view, int loop, int cel, int x, int y, int pri, in
eraseBoth();
- debugC(4, kDebugLevelSprites, "blit_cel (%d, %d, %d, c)", x, y, pri);
+ debugC(4, kDebugLevelSprites, "blitCel(%d, %d, %d, c)", x, y, pri);
blitCel(x1, y1, pri, c, _vm->_game.views[view].agi256_2);
// If margin is 0, 1, 2, or 3, the base of the cel is
@@ -659,8 +651,7 @@ void SpritesMgr::addToPic(int view, int loop, int cel, int x, int y, int pri, in
blitBoth();
- debugC(4, kDebugLevelSprites, "commit_block (%d, %d, %d, %d)", x1, y1, x2, y2);
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, true);
}
/**
@@ -688,18 +679,19 @@ void SpritesMgr::showObj(int n) {
s.xSize = c->width;
s.ySize = c->height;
s.buffer = (uint8 *)malloc(s.xSize * s.ySize);
+ s.v = 0;
objsSaveArea(&s);
blitCel(x1, y1, 15, c, _vm->_game.views[n].agi256_2);
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, true);
_vm->messageBox(_vm->_game.views[n].descr);
objsRestoreArea(&s);
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, true);
free(s.buffer);
}
-void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2) {
+void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2, bool immediate) {
int i, w, offset;
uint8 *q;
@@ -711,7 +703,7 @@ void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2) {
y1 = CLIP(y1, 0, _HEIGHT - 1);
y2 = CLIP(y2, 0, _HEIGHT - 1);
- debugC(7, kDebugLevelSprites, "%d, %d, %d, %d", x1, y1, x2, y2);
+ debugC(7, kDebugLevelSprites, "commitBlock(%d, %d, %d, %d)", x1, y1, x2, y2);
w = x2 - x1 + 1;
q = &_vm->_game.sbuf16c[x1 + _WIDTH * y1];
@@ -723,6 +715,9 @@ void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2) {
}
_gfx->flushBlockA(x1, y1 + offset, x2, y2 + offset);
+
+ if (immediate)
+ _gfx->doUpdate();
}
SpritesMgr::SpritesMgr(AgiEngine *agi, GfxMgr *gfx) {
diff --git a/engines/agi/sprite.h b/engines/agi/sprite.h
index 7d6d7bb97e..57fd0dacf2 100644
--- a/engines/agi/sprite.h
+++ b/engines/agi/sprite.h
@@ -65,7 +65,7 @@ private:
void buildUpdBlitlist();
void buildNonupdBlitlist();
void freeList(SpriteList &l);
- void commitSprites(SpriteList &l);
+ void commitSprites(SpriteList &l, bool immediate = false);
void eraseSprites(SpriteList &l);
void blitSprites(SpriteList &l);
static bool testUpdating(VtEntry *v, AgiEngine *);
@@ -88,7 +88,7 @@ public:
void commitBoth();
void addToPic(int, int, int, int, int, int, int);
void showObj(int);
- void commitBlock(int, int, int, int);
+ void commitBlock(int x1, int y1, int x2, int y2, bool immediate = false);
};
} // End of namespace Agi
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index ee9aebf240..778da0a527 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -135,8 +135,8 @@ void AgiEngine::blitTextbox(const char *p, int y, int x, int len) {
if (x == 0 && y == 0 && len == 0)
x = y = -1;
- if (len <= 0 || len >= 40)
- len = 32;
+ if (len <= 0)
+ len = 30;
xoff = x * CHAR_COLS;
yoff = y * CHAR_LINES;
@@ -214,6 +214,7 @@ void AgiEngine::printTextConsole(const char *msg, int x, int y, int len, int fg,
x *= CHAR_COLS;
y *= 10;
+ debugC(4, kDebugLevelText, "printTextConsole(): %s, %d, %d, %d, %d, %d", msg, x, y, len, fg, bg);
printText2(1, msg, 0, x, y, len, fg, bg);
}
@@ -488,7 +489,7 @@ int AgiEngine::print(const char *p, int lin, int col, int len) {
_game.keypress = 0;
break;
}
- } while (_game.msgBoxTicks > 0);
+ } while (_game.msgBoxTicks > 0 && !(shouldQuit() || _restartGame));
setvar(vWindowReset, 0);
@@ -655,11 +656,8 @@ void AgiEngine::writePrompt() {
int l, fg, bg, pos;
int promptLength = strlen(agiSprintf(_game.strings[0]));
- if (!_game.inputEnabled || _game.inputMode != INPUT_NORMAL) {
- clearPrompt();
-
+ if (!_game.inputEnabled || _game.inputMode != INPUT_NORMAL)
return;
- }
l = _game.lineUserInput;
fg = _game.colorFg;
@@ -699,6 +697,8 @@ void AgiEngine::clearLines(int l1, int l2, int c) {
// inc for endline so it matches the correct num
// ie, from 22 to 24 is 3 lines, not 2 lines.
+ debugC(4, kDebugLevelText, "clearLines(%d, %d, %d)", l1, l2, c);
+
l1 *= CHAR_LINES;
l2 *= CHAR_LINES;
l2 += CHAR_LINES - 1;
diff --git a/engines/agi/view.cpp b/engines/agi/view.cpp
index fb417e86a9..fcca1e2a79 100644
--- a/engines/agi/view.cpp
+++ b/engines/agi/view.cpp
@@ -157,8 +157,7 @@ int AgiEngine::decodeView(int n) {
return errNoLoopsInView;
// allocate memory for all views
- _game.views[n].loop = (ViewLoop *)
- calloc(_game.views[n].numLoops, sizeof(ViewLoop));
+ _game.views[n].loop = (ViewLoop *)calloc(_game.views[n].numLoops, sizeof(ViewLoop));
if (_game.views[n].loop == NULL)
return errNotEnoughMemory;
@@ -294,37 +293,10 @@ void AgiEngine::setLoop(VtEntry *v, int n) {
* @param n number of AGI view resource
*/
void AgiEngine::setView(VtEntry *v, int n) {
-
- uint16 viewFlags = 0;
-
- // When setting a view to the view table, if there's already another view set in that
- // view table entry and it's still drawn, erase the existing view before setting the new one
- // Fixes bug #1658643: AGI: SQ1 (2.2 DOS ENG) Graphic error, ego leaves behind copy
- // Update: Apparently, this makes ego dissapear at times, e.g. when textboxes are shown
- // Therefore, it's limited to view 118 in SQ1 (Roger climbing the ladder)
- // Fixes bug #1715284: Roger sometimes disappears
- if (v->viewData != NULL) {
- if (v->currentView == 118 && v->flags & DRAWN && getGameID() == GID_SQ1) {
- viewFlags = v->flags; // Store the flags for the view
- _sprites->eraseUpdSprites();
-
- if (v->flags & UPDATE) {
- v->flags &= ~DRAWN;
- } else {
- _sprites->eraseNonupdSprites();
- v->flags &= ~DRAWN;
- _sprites->blitNonupdSprites();
- }
- _sprites->blitUpdSprites();
-
- _sprites->commitBlock(v->xPos, v->yPos - v->ySize + 1, v->xPos + v->xSize - 1, v->yPos);
- v->flags = viewFlags; // Restore the view's flags
- }
- }
-
v->viewData = &_game.views[n];
v->currentView = n;
v->numLoops = v->viewData->numLoops;
+ v->viewReplaced = true;
setLoop(v, v->currentLoop >= v->numLoops ? 0 : v->currentLoop);
}
@@ -338,6 +310,7 @@ void AgiEngine::startUpdate(VtEntry *v) {
v->flags |= UPDATE;
_sprites->blitBoth();
+ _sprites->commitBoth();
}
}
@@ -351,6 +324,7 @@ void AgiEngine::stopUpdate(VtEntry *v) {
v->flags &= ~UPDATE;
_sprites->blitBoth();
+ _sprites->commitBoth();
}
}
@@ -393,7 +367,7 @@ void AgiEngine::updateViewtable() {
break;
default:
// for KQ4
- if (getVersion() == 0x3086)
+ if (getVersion() == 0x3086 || getGameID() == GID_KQ4)
loop = loopTable4[v->direction];
break;
}
diff --git a/engines/agi/view.h b/engines/agi/view.h
index f9017ec4ae..85f2d6eaf9 100644
--- a/engines/agi/view.h
+++ b/engines/agi/view.h
@@ -63,6 +63,7 @@ struct VtEntry {
int16 xPos;
int16 yPos;
uint8 currentView;
+ bool viewReplaced;
struct AgiView *viewData;
uint8 currentLoop;
uint8 numLoops;
diff --git a/engines/agi/wagparser.cpp b/engines/agi/wagparser.cpp
index 1d60524070..22de66712d 100644
--- a/engines/agi/wagparser.cpp
+++ b/engines/agi/wagparser.cpp
@@ -100,10 +100,8 @@ void WagProperty::setDefaults() {
}
void WagProperty::deleteData() {
- if (_propData != NULL) {
- delete _propData;
- _propData = NULL;
- }
+ delete _propData;
+ _propData = NULL;
}
WagFileParser::WagFileParser() :
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
index c8b22956f4..464c218ae8 100644
--- a/engines/agi/words.cpp
+++ b/engines/agi/words.cpp
@@ -76,10 +76,8 @@ int AgiEngine::loadWords(const char *fname) {
}
void AgiEngine::unloadWords() {
- if (words != NULL) {
- free(words);
- words = NULL;
- }
+ free(words);
+ words = NULL;
}
/**
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index 926b3a8972..670c701198 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -202,19 +202,19 @@ AGOSEngine::AGOSEngine(OSystem *syst)
_scanFlag = false;
_scriptVar2 = 0;
- _runScriptReturn1 = 0;
- _skipVgaWait = 0;
- _noParentNotify = 0;
- _beardLoaded = 0;
- _litBoxFlag = 0;
- _mortalFlag = 0;
+ _runScriptReturn1 = false;
+ _skipVgaWait = false;
+ _noParentNotify = false;
+ _beardLoaded = false;
+ _litBoxFlag = false;
+ _mortalFlag = false;
_displayFlag = 0;
- _syncFlag2 = 0;
- _inCallBack = 0;
- _cepeFlag = 0;
- _fastMode = 0;
+ _syncFlag2 = false;
+ _inCallBack = false;
+ _cepeFlag = false;
+ _fastMode = false;
- _backFlag = 0;
+ _backFlag = false;
_debugMode = 0;
_dumpScripts = false;
@@ -557,10 +557,10 @@ Common::Error AGOSEngine::init() {
(getPlatform() == Common::kPlatformPC)) {
// Setup midi driver
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI);
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | (getGameType() == GType_SIMON1 ? MDT_PREFER_MT32 : MDT_PREFER_GM));
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (_nativeMT32) {
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
@@ -631,9 +631,11 @@ Common::Error AGOSEngine::init() {
if (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute") == 1) {
if (getGameId() == GID_SIMON1DOS)
- _midi._enable_sfx ^= 1;
- else
- _sound->effectsPause(_effectsPaused ^= 1);
+ _midi._enable_sfx = !_midi._enable_sfx;
+ else {
+ _effectsPaused = !_effectsPaused;
+ _sound->effectsPause(_effectsPaused);
+ }
}
_copyProtection = ConfMan.getBool("copy_protection");
@@ -648,7 +650,7 @@ Common::Error AGOSEngine::init() {
if (getGameType() == GType_SIMON1) {
// English and German versions don't have full subtitles
- if (_language == Common::EN_ANY || _language == Common::DE_DEU)
+ if (_language == Common::EN_ANY || _language == Common::DE_DEU)
_subtitles = false;
// Other versions require speech to be enabled
else
@@ -1037,12 +1039,21 @@ uint32 AGOSEngine::getTime() const {
}
void AGOSEngine::syncSoundSettings() {
- _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"));
+ // 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");
+
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, (mute ? 0 : (_musicPaused ? 0 : soundVolumeMusic)));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, (mute ? 0 : soundVolumeSFX));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, (mute ? 0 : soundVolumeSpeech));
if (_midiEnabled)
- _midi.setVolume(ConfMan.getInt("music_volume"), ConfMan.getInt("sfx_volume"));
+ _midi.setVolume((mute ? 0 : soundVolumeMusic), (mute ? 0 : soundVolumeSFX));
}
} // End of namespace AGOS
diff --git a/engines/agos/agos.h b/engines/agos/agos.h
index ab1009e02a..b12bf09d62 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -393,7 +393,7 @@ protected:
Common::Point _mouseOld;
byte *_mouseData;
- byte _animatePointer;
+ bool _animatePointer;
byte _maxCursorWidth, _maxCursorHeight;
byte _mouseAnim, _mouseAnimMax, _mouseCursor;
byte _currentMouseAnim, _currentMouseCursor;
diff --git a/engines/agos/cursor.cpp b/engines/agos/cursor.cpp
index 109184e9c7..5ff2f014a6 100644
--- a/engines/agos/cursor.cpp
+++ b/engines/agos/cursor.cpp
@@ -459,7 +459,7 @@ void AGOSEngine_Simon1::handleMouseMoved() {
_leftButtonDown = false;
x = 1;
} else {
- if (_litBoxFlag == 0 && _needHitAreaRecalc == 0)
+ if (!_litBoxFlag && _needHitAreaRecalc == 0)
goto get_out;
}
@@ -473,7 +473,7 @@ get_out:
drawMousePointer();
_needHitAreaRecalc = 0;
- _litBoxFlag = 0;
+ _litBoxFlag = false;
}
void AGOSEngine_PN::handleMouseMoved() {
@@ -538,7 +538,7 @@ void AGOSEngine_PN::handleMouseMoved() {
drawMousePointer();
_needHitAreaRecalc = 0;
- _litBoxFlag = 0;
+ _litBoxFlag = false;
}
void AGOSEngine::handleMouseMoved() {
@@ -610,7 +610,7 @@ void AGOSEngine::handleMouseMoved() {
_oneClick = 0;
x = 1;
} else {
- if (_litBoxFlag == 0 && _needHitAreaRecalc == 0)
+ if (!_litBoxFlag && _needHitAreaRecalc == 0)
goto get_out;
}
@@ -622,7 +622,7 @@ get_out:
drawMousePointer();
_needHitAreaRecalc = 0;
- _litBoxFlag = 0;
+ _litBoxFlag = false;
}
void AGOSEngine::mouseOff() {
@@ -706,10 +706,10 @@ void AGOSEngine_Feeble::drawMousePointer() {
uint cursor;
int image, offs;
- if (_animatePointer != 0) {
+ if (_animatePointer) {
if (getBitFlag(99)) {
- _mouseToggle ^= 1;
- if (_mouseToggle != 0)
+ _mouseToggle = !_mouseToggle;
+ if (_mouseToggle)
_mouseAnim++;
} else {
_mouseAnim++;
@@ -720,7 +720,7 @@ void AGOSEngine_Feeble::drawMousePointer() {
cursor = _mouseCursor;
- if (_animatePointer == 0 && getBitFlag(99)) {
+ if (!_animatePointer && getBitFlag(99)) {
_mouseAnim = 1;
cursor = 6;
} else if (_mouseCursor != 5 && getBitFlag(72)) {
diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp
index 39974f9d53..646e63dacf 100644
--- a/engines/agos/detection.cpp
+++ b/engines/agos/detection.cpp
@@ -84,6 +84,11 @@ static const PlainGameDescriptor simonGames[] = {
#include "agos/detection_tables.h"
+static const char *directoryGlobs[] = {
+ "execute", // Used by Simon1 Acorn CD
+ 0
+};
+
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)AGOS::gameDescriptions,
@@ -102,7 +107,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOLAUNCHLOAD
+ Common::GUIO_NOLAUNCHLOAD,
+ // Maximum directory depth
+ 2,
+ // List of directory globs
+ directoryGlobs
};
using namespace AGOS;
diff --git a/engines/agos/draw.cpp b/engines/agos/draw.cpp
index 300ed4c52b..4d32b4521d 100644
--- a/engines/agos/draw.cpp
+++ b/engines/agos/draw.cpp
@@ -176,9 +176,9 @@ void AGOSEngine::animateSprites() {
_windowNum = 4;
- _backFlag = 1;
+ _backFlag = true;
drawImage(&state);
- _backFlag = 0;
+ _backFlag = false;
_vgaSpriteChanged++;
}
@@ -451,14 +451,14 @@ void AGOSEngine::restoreBackGround() {
state.paletteMod = 0;
state.flags = kDFNonTrans;
- _backFlag = 1;
+ _backFlag = true;
drawImage(&state);
if (getGameType() != GType_SIMON1 && getGameType() != GType_SIMON2) {
animTable->srcPtr = 0;
}
}
- _backFlag = 0;
+ _backFlag = false;
if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
AnimTable *animTableTmp;
diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp
index 1340d85236..95c9db906a 100644
--- a/engines/agos/event.cpp
+++ b/engines/agos/event.cpp
@@ -431,8 +431,7 @@ void AGOSEngine::delay(uint amount) {
AudioCD.updateCD();
- if (_debugger->isAttached())
- _debugger->onFrame();
+ _debugger->onFrame();
vgaPeriod = (_fastMode) ? 10 : _vgaPeriod;
if (getGameType() == GType_PP && getGameId() != GID_DIMP) {
@@ -481,7 +480,7 @@ void AGOSEngine::delay(uint amount) {
_aboutDialog = new GUI::AboutDialog();
_aboutDialog->runModal();
} else if (event.kbd.keycode == Common::KEYCODE_f) {
- _fastMode ^= 1;
+ _fastMode = !_fastMode;
} else if (event.kbd.keycode == Common::KEYCODE_d) {
_debugger->attach();
} else if (event.kbd.keycode == Common::KEYCODE_s) {
@@ -568,7 +567,7 @@ void AGOSEngine_Feeble::timerProc() {
_videoLockOut |= 2;
if (!(_videoLockOut & 0x10)) {
- _syncFlag2 ^= 1;
+ _syncFlag2 = !_syncFlag2;
if (!_syncFlag2) {
processVgaEvents();
} else {
@@ -637,7 +636,7 @@ void AGOSEngine_PN::timerProc() {
processVgaEvents();
processVgaEvents();
- _cepeFlag ^= 1;
+ _cepeFlag = !_cepeFlag;
if (!_cepeFlag)
processVgaEvents();
}
@@ -663,7 +662,7 @@ void AGOSEngine::timerProc() {
if (!(_videoLockOut & 0x10)) {
processVgaEvents();
processVgaEvents();
- _cepeFlag ^= 1;
+ _cepeFlag = !_cepeFlag;
if (!_cepeFlag)
processVgaEvents();
}
diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp
index e2c634007c..82a4cb714b 100644
--- a/engines/agos/gfx.cpp
+++ b/engines/agos/gfx.cpp
@@ -730,7 +730,7 @@ void AGOSEngine_Simon1::drawImage(VC10_state *state) {
state->paletteMod = 208;
}
- if (_backFlag == 1) {
+ if (_backFlag) {
drawBackGroundImage(state);
} else if (state->flags & kDFMasked) {
drawMaskedImage(state);
@@ -947,7 +947,7 @@ void AGOSEngine::drawImage(VC10_state *state) {
if (getGameType() == GType_ELVIRA2 && getPlatform() == Common::kPlatformAtariST && yoffs > 133)
state->palette = 208;
- if (_backFlag == 1) {
+ if (_backFlag) {
drawBackGroundImage(state);
} else {
drawVertImage(state);
@@ -1351,7 +1351,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
if (getGameType() == GType_FF || getGameType() == GType_PP) {
fillBackGroundFromBack();
- _syncFlag2 = 1;
+ _syncFlag2 = true;
} else {
_copyScnFlag = 2;
_vgaSpriteChanged++;
diff --git a/engines/agos/icons.cpp b/engines/agos/icons.cpp
index fdc5d1707e..08a3d4e2f0 100644
--- a/engines/agos/icons.cpp
+++ b/engines/agos/icons.cpp
@@ -448,7 +448,7 @@ void AGOSEngine_Feeble::drawIconArray(uint num, Item *itemRef, int line, int cla
setupIconHitArea(window, k++, xp, yp, itemRef);
} else {
/*
- * Just remember the overflow has occured
+ * Just remember the overflow has occurred
*/
window->iconPtr->iconArray[icount].item = NULL; /* END MARKINGS */
_iOverflow = 1;
diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp
index 35ed045675..1246149aa5 100644
--- a/engines/agos/input.cpp
+++ b/engines/agos/input.cpp
@@ -99,7 +99,7 @@ void AGOSEngine::setup_cond_c_helper() {
animMax = 9;
}
- _animatePointer = 0;
+ _animatePointer = false;
_mouseCursor = cursor;
_mouseAnimMax = animMax;
_mouseAnim = 1;
@@ -574,13 +574,13 @@ bool AGOSEngine::processSpecialKeys() {
if (getGameType() == GType_FF || (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE)) ||
((getFeatures() & GF_TALKIE) && _language != Common::EN_ANY && _language != Common::DE_DEU)) {
if (_speech)
- _subtitles ^= 1;
+ _subtitles = !_subtitles;
}
break;
case 'v':
if (getGameType() == GType_FF || (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE))) {
if (_subtitles)
- _speech ^= 1;
+ _speech = !_speech;
}
break;
case '+':
@@ -598,22 +598,24 @@ bool AGOSEngine::processSpecialKeys() {
syncSoundSettings();
break;
case 'm':
- _musicPaused ^= 1;
+ _musicPaused = !_musicPaused;
if (_midiEnabled) {
_midi.pause(_musicPaused);
}
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, (_musicPaused) ? 0 : ConfMan.getInt("music_volume"));
+ syncSoundSettings();
break;
case 's':
if (getGameId() == GID_SIMON1DOS) {
- _midi._enable_sfx ^= 1;
+ _midi._enable_sfx = !_midi._enable_sfx;
} else {
- _sound->effectsPause(_effectsPaused ^= 1);
+ _effectsPaused = !_effectsPaused;
+ _sound->effectsPause(_effectsPaused);
}
break;
case 'b':
if (getGameType() == GType_SIMON2) {
- _sound->ambientPause(_ambientPaused ^= 1);
+ _ambientPaused = !_ambientPaused;
+ _sound->ambientPause(_ambientPaused);
}
break;
default:
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index ed3e3d801b..ab5bfc4c94 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -346,15 +346,11 @@ void MidiPlayer::clearConstructs(MusicInfo &info) {
info.num_songs = 0;
}
- if (info.data) {
- free(info.data);
- info.data = 0;
- } // end if
-
- if (info.parser) {
- delete info.parser;
- info.parser = 0;
- }
+ free(info.data);
+ info.data = 0;
+
+ delete info.parser;
+ info.parser = 0;
if (_driver) {
for (i = 0; i < 16; ++i) {
diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp
index a85c1627bf..b05bac1e57 100644
--- a/engines/agos/verb.cpp
+++ b/engines/agos/verb.cpp
@@ -207,7 +207,7 @@ static const char *const czech_verb_prep_names[] = {
void AGOSEngine_Feeble::clearName() {
stopAnimateSimon2(2, 6);
_lastNameOn = NULL;
- _animatePointer = 0;
+ _animatePointer = false;
_mouseAnim = 1;
return;
}
@@ -898,7 +898,7 @@ void AGOSEngine::displayName(HitArea *ha) {
if (getBitFlag(99))
_animatePointer = ((ha->flags & kBFTextBox) == 0);
else
- _animatePointer = 1;
+ _animatePointer = true;
if (!getBitFlag(73))
return;
@@ -933,7 +933,7 @@ void AGOSEngine_Feeble::invertBox(HitArea *ha, bool state) {
_mouseCursor = _oldMouseCursor;
} else if (_mouseCursor != 18) {
_oldMouseCursor = _mouseCursor;
- _animatePointer = 0;
+ _animatePointer = false;
_oldMouseAnimMax = _mouseAnimMax;
_mouseAnimMax = 2;
_mouseCursor = 18;
diff --git a/engines/agos/vga_e2.cpp b/engines/agos/vga_e2.cpp
index 1bbc7f4849..54ec45b967 100644
--- a/engines/agos/vga_e2.cpp
+++ b/engines/agos/vga_e2.cpp
@@ -265,7 +265,7 @@ void AGOSEngine::vc53_dissolveIn() {
*dst &= color;
*dst |= *src & 0xF;
- _system->unlockScreen();
+ _system->unlockScreen();
dissolveCount--;
if (!dissolveCount) {
@@ -319,7 +319,7 @@ void AGOSEngine::vc54_dissolveOut() {
dst += xoffs;
*dst = color;
- _system->unlockScreen();
+ _system->unlockScreen();
dissolveCount--;
if (!dissolveCount) {
@@ -388,7 +388,7 @@ void AGOSEngine::vc56_fullScreen() {
src += 320;
dst += screen->pitch;
}
- _system->unlockScreen();
+ _system->unlockScreen();
fullFade();
}
diff --git a/engines/agos/vga_s2.cpp b/engines/agos/vga_s2.cpp
index 4eb739e974..db3a7c18f3 100644
--- a/engines/agos/vga_s2.cpp
+++ b/engines/agos/vga_s2.cpp
@@ -138,7 +138,7 @@ void AGOSEngine::vc69_playSeq() {
// 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
+ // "fill MIDI stream" event occurred, the MIDI
// player would find the change and switch
// tracks. We use a different architecture that
// allows for an immediate response here, but
diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp
index 5ddc5d625f..747c9221ee 100644
--- a/engines/cine/anim.cpp
+++ b/engines/cine/anim.cpp
@@ -49,8 +49,6 @@ struct AnimHeader2Struct {
uint16 field_E;
};
-Common::Array<AnimData> animDataTable;
-
static const AnimDataEntry transparencyData[] = {
{"ALPHA", 0xF},
{"TITRE2", 0xF},
@@ -400,7 +398,7 @@ void AnimData::save(Common::OutSaveFile &fHandle) const {
*/
void freeAnimDataRange(byte startIdx, byte numIdx) {
for (byte i = 0; i < numIdx; i++) {
- animDataTable[startIdx + i].clear();
+ g_cine->_animDataTable[startIdx + i].clear();
}
}
@@ -514,7 +512,7 @@ void loadAnimHeader(AnimHeaderStruct &animHeader, Common::MemoryReadStream readS
*/
int emptyAnimSpace(int start = 0) {
for (; start < NUM_MAX_ANIMDATA; start++) {
- if (!animDataTable[start].data()) {
+ if (!g_cine->_animDataTable[start].data()) {
return start;
}
}
@@ -540,7 +538,7 @@ int loadSpl(const char *resourceName, int16 idx) {
entry = idx < 0 ? emptyAnimSpace() : idx;
assert(entry >= 0);
- animDataTable[entry].load(dataPtr, ANIM_RAW, partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName);
+ g_cine->_animDataTable[entry].load(dataPtr, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
return entry + 1;
@@ -570,7 +568,7 @@ int loadMsk(const char *resourceName, int16 idx) {
entry = idx < 0 ? emptyAnimSpace() : idx;
assert(entry >= 0);
for (int16 i = 0; i < animHeader.numFrames; i++, entry++) {
- animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName);
+ g_cine->_animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName);
ptr += animHeader.frameWidth * animHeader.frameHeight;
}
@@ -621,7 +619,7 @@ int loadAni(const char *resourceName, int16 idx) {
transparentColor = i < 1 ? 0xE : 0;
}
- animDataTable[entry].load(ptr, ANIM_MASKSPRITE, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName, transparentColor);
+ g_cine->_animDataTable[entry].load(ptr, ANIM_MASKSPRITE, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName, transparentColor);
ptr += animHeader.frameWidth * animHeader.frameHeight;
}
@@ -737,7 +735,7 @@ int loadSet(const char *resourceName, int16 idx) {
type = ANIM_FULLSPRITE;
}
- animDataTable[entry].load(dataPtr, type, header2.width, header2.height, foundFileIdx, i, currentPartName);
+ g_cine->_animDataTable[entry].load(dataPtr, type, header2.width, header2.height, foundFileIdx, i, currentPartName);
}
free(origDataPtr);
@@ -759,7 +757,7 @@ int loadSeq(const char *resourceName, int16 idx) {
byte *dataPtr = readBundleFile(foundFileIdx);
int entry = idx < 0 ? emptyAnimSpace() : idx;
- animDataTable[entry].load(dataPtr+0x16, ANIM_RAW, partBuffer[foundFileIdx].unpackedSize-0x16, 1, foundFileIdx, 0, currentPartName);
+ g_cine->_animDataTable[entry].load(dataPtr+0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize-0x16, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
return entry + 1;
}
diff --git a/engines/cine/anim.h b/engines/cine/anim.h
index e67237aac0..2012ef875e 100644
--- a/engines/cine/anim.h
+++ b/engines/cine/anim.h
@@ -99,8 +99,6 @@ public:
#define NUM_MAX_ANIMDATA 255
-extern Common::Array<AnimData> animDataTable;
-
void freeAnimDataTable();
void freeAnimDataRange(byte startIdx, byte numIdx);
int loadResource(const char *resourceName, int16 idx = -1);
diff --git a/engines/cine/bg_list.cpp b/engines/cine/bg_list.cpp
index 26351becf1..3ecda4125d 100644
--- a/engines/cine/bg_list.cpp
+++ b/engines/cine/bg_list.cpp
@@ -36,14 +36,13 @@
namespace Cine {
uint32 var8;
-Common::List<BGIncrust> bgIncrustList;
/**
* Add masked sprite to the background
* @param objIdx Sprite description
*/
void addToBGList(int16 objIdx) {
- renderer->incrustSprite(objectTable[objIdx]);
+ renderer->incrustSprite(g_cine->_objectTable[objIdx]);
createBgIncrustListElement(objIdx, 0);
}
@@ -53,7 +52,7 @@ void addToBGList(int16 objIdx) {
* @param objIdx Sprite description
*/
void addSpriteFilledToBGList(int16 objIdx) {
- renderer->incrustMask(objectTable[objIdx]);
+ renderer->incrustMask(g_cine->_objectTable[objIdx]);
createBgIncrustListElement(objIdx, 1);
}
@@ -69,12 +68,12 @@ void createBgIncrustListElement(int16 objIdx, int16 param) {
tmp.unkPtr = 0;
tmp.objIdx = objIdx;
tmp.param = param;
- tmp.x = objectTable[objIdx].x;
- tmp.y = objectTable[objIdx].y;
- tmp.frame = objectTable[objIdx].frame;
- tmp.part = objectTable[objIdx].part;
+ tmp.x = g_cine->_objectTable[objIdx].x;
+ tmp.y = g_cine->_objectTable[objIdx].y;
+ tmp.frame = g_cine->_objectTable[objIdx].frame;
+ tmp.part = g_cine->_objectTable[objIdx].part;
- bgIncrustList.push_back(tmp);
+ g_cine->_bgIncrustList.push_back(tmp);
}
/**
@@ -104,12 +103,12 @@ void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle) {
tmp.frame = fHandle.readUint16BE();
tmp.part = fHandle.readUint16BE();
- bgIncrustList.push_back(tmp);
+ g_cine->_bgIncrustList.push_back(tmp);
if (tmp.param == 0) {
- renderer->incrustSprite(objectTable[tmp.objIdx]);
+ renderer->incrustSprite(g_cine->_objectTable[tmp.objIdx]);
} else {
- renderer->incrustMask(objectTable[tmp.objIdx]);
+ renderer->incrustMask(g_cine->_objectTable[tmp.objIdx]);
}
}
}
diff --git a/engines/cine/bg_list.h b/engines/cine/bg_list.h
index 409b16dbd5..8c1b3cb6ec 100644
--- a/engines/cine/bg_list.h
+++ b/engines/cine/bg_list.h
@@ -42,7 +42,6 @@ struct BGIncrust {
int16 part;
};
-extern Common::List<BGIncrust> bgIncrustList;
extern uint32 var8;
void addToBGList(int16 objIdx);
diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp
index c50af52901..5c2119c1e4 100644
--- a/engines/cine/cine.cpp
+++ b/engines/cine/cine.cpp
@@ -48,10 +48,9 @@
namespace Cine {
-Sound *g_sound;
-Common::SaveFileManager *g_saveFileMan;
+Sound *g_sound = 0;
-CineEngine *g_cine;
+CineEngine *g_cine = 0;
CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
DebugMan.addDebugChannel(kCineDebugScript, "Script", "Script debug level");
@@ -72,7 +71,7 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng
}
CineEngine::~CineEngine() {
- if (g_cine->getGameType() == Cine::GType_OS) {
+ if (getGameType() == Cine::GType_OS) {
freeErrmessDat();
}
DebugMan.clearAllDebugChannels();
@@ -82,13 +81,12 @@ Common::Error CineEngine::run() {
// Initialize backend
initGraphics(320, 200, false);
- if (g_cine->getPlatform() == Common::kPlatformPC) {
+ if (getPlatform() == Common::kPlatformPC) {
g_sound = new PCSound(_mixer, this);
} else {
// Paula chipset for Amiga and Atari versions
g_sound = new PaulaSound(_mixer, this);
}
- g_saveFileMan = _saveFileMan;
_restartRequested = false;
@@ -125,31 +123,33 @@ int CineEngine::modifyGameSpeed(int speedChange) {
}
void CineEngine::initialize() {
+ _globalVars.reinit(NUM_MAX_VAR + 1);
+
// Initialize all savegames' descriptions to empty strings
memset(currentSaveName, 0, sizeof(currentSaveName));
// Resize object table to its correct size and reset all its elements
- objectTable.resize(NUM_MAX_OBJECT);
+ g_cine->_objectTable.resize(NUM_MAX_OBJECT);
resetObjectTable();
// Resize animation data table to its correct size and reset all its elements
- animDataTable.resize(NUM_MAX_ANIMDATA);
+ g_cine->_animDataTable.resize(NUM_MAX_ANIMDATA);
freeAnimDataTable();
// Resize zone data table to its correct size and reset all its elements
- zoneData.resize(NUM_MAX_ZONE);
- Common::set_to(zoneData.begin(), zoneData.end(), 0);
+ g_cine->_zoneData.resize(NUM_MAX_ZONE);
+ Common::set_to(g_cine->_zoneData.begin(), g_cine->_zoneData.end(), 0);
// Resize zone query table to its correct size and reset all its elements
- zoneQuery.resize(NUM_MAX_ZONE);
- Common::set_to(zoneQuery.begin(), zoneQuery.end(), 0);
+ g_cine->_zoneQuery.resize(NUM_MAX_ZONE);
+ Common::set_to(g_cine->_zoneQuery.begin(), g_cine->_zoneQuery.end(), 0);
_timerDelayMultiplier = 12; // Set default speed
setupOpcodes();
- initLanguage(g_cine->getLanguage());
+ initLanguage(getLanguage());
- if (g_cine->getGameType() == Cine::GType_OS) {
+ if (getGameType() == Cine::GType_OS) {
renderer = new OSRenderer;
} else {
renderer = new FWRenderer;
@@ -161,28 +161,28 @@ void CineEngine::initialize() {
// Clear part buffer as there's nothing loaded into it yet.
// Its size will change when loading data into it with the loadPart function.
- partBuffer.clear();
+ g_cine->_partBuffer.clear();
- if (g_cine->getGameType() == Cine::GType_OS) {
+ if (getGameType() == Cine::GType_OS) {
readVolCnf();
}
loadTextData("texte.dat");
- if (g_cine->getGameType() == Cine::GType_OS && !(g_cine->getFeatures() & GF_DEMO)) {
+ if (getGameType() == Cine::GType_OS && !(getFeatures() & GF_DEMO)) {
loadPoldatDat("poldat.dat");
loadErrmessDat("errmess.dat");
}
// in case ScummVM engines can be restarted in the future
- scriptTable.clear();
- relTable.clear();
- objectScripts.clear();
- globalScripts.clear();
- bgIncrustList.clear();
+ g_cine->_scriptTable.clear();
+ g_cine->_relTable.clear();
+ g_cine->_objectScripts.clear();
+ g_cine->_globalScripts.clear();
+ g_cine->_bgIncrustList.clear();
freeAnimDataTable();
- overlayList.clear();
- messageTable.clear();
+ g_cine->_overlayList.clear();
+ g_cine->_messageTable.clear();
resetObjectTable();
var8 = 0;
diff --git a/engines/cine/cine.h b/engines/cine/cine.h
index 6f7b409ad7..114d98d442 100644
--- a/engines/cine/cine.h
+++ b/engines/cine/cine.h
@@ -47,6 +47,8 @@
#include "cine/pal.h"
#include "cine/gfx.h"
#include "cine/anim.h"
+#include "cine/bg_list.h"
+#include "cine/various.h"
//#define DUMP_SCRIPTS
@@ -93,6 +95,7 @@ enum CineGameFeatures {
};
struct CINEGameDescription;
+struct SeqListElement;
typedef Common::HashMap<Common::String, const char *> StringPtrHashMap;
@@ -150,6 +153,36 @@ private:
bool _preLoad;
int _timerDelayMultiplier;
+
+ public:
+ // TODO: These are pseudo-global vars
+ // They better belong to appropriate classes
+ Common::Array<AnimData> _animDataTable;
+ Common::List<BGIncrust> _bgIncrustList;
+ Common::StringArray _messageTable;
+ Common::Array<ObjectStruct> _objectTable;
+ Common::List<overlay> _overlayList;
+ Common::Array<PalEntry> _palArray;
+ Common::Array<PartBuffer> _partBuffer;
+ ScriptList _globalScripts;
+ ScriptList _objectScripts;
+ RawObjectScriptArray _relTable; ///< Object script bytecode table
+
+ /**
+ * Global variables.
+ * 255 of these are saved, but there's one more that's used for bypassing the copy protection.
+ * In CineEngine::mainLoop(int bootScriptIdx) there's this code: globalVars[VAR_BYPASS_PROTECTION] = 0;
+ * And as VAR_BYPASS_PROTECTION is 255 that's why we're allocating one more than we otherwise would.
+ */
+ ScriptVars _globalVars;
+ RawScriptArray _scriptTable; ///< Table of script bytecode
+
+ Common::Array<uint16> _zoneData;
+ Common::Array<uint16> _zoneQuery; ///< Only exists in Operation Stealth
+
+ Common::List<SeqListElement> _seqList;
+
+ Common::String _commandBuffer;
};
extern CineEngine *g_cine;
@@ -190,8 +223,6 @@ enum {
};
-extern Common::SaveFileManager *g_saveFileMan; // TEMP
-
} // End of namespace Cine
#endif
diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp
index fcfa1f7f20..9dfa2f71ea 100644
--- a/engines/cine/detection.cpp
+++ b/engines/cine/detection.cpp
@@ -23,8 +23,6 @@
*
*/
-
-
#include "base/plugins.h"
#include "engines/advancedDetector.h"
@@ -62,494 +60,7 @@ static const ADObsoleteGameID obsoleteGameIDsTable[] = {
{0, 0, Common::kPlatformUnknown}
};
-namespace Cine {
-
-using Common::GUIO_NONE;
-
-static const CINEGameDescription gameDescriptions[] = {
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "61d003202d301c29dd399acfb1354310"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- // This is a CD version of Future Wars published by Sony.
- // This version has a crypted AUTO00.PRC.
- {
- {
- "fw",
- "Sony CD version",
- {
- { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1},
- { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1},
- { NULL, 0, NULL, 0}
- },
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_CD,
- GUIO_NONE
- },
- GType_FW,
- GF_CD | GF_CRYPTED_BOOT_PRC,
- },
-
- {
- // This is the version included in the UK "Classic Collection"
- {
- "fw",
- "",
- AD_ENTRY1("part01", "91d7271155520eae6915a9dd2dac120c"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "f5e98fcca3fb5e7afa284c81c39d8b14"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- GF_ALT_FONT,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "570109f965c7f53984b98c83d86eb206"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- GF_ALT_FONT,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "5d1acb97abe9591f9008e00d07add95a"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "57afd280b598b4180fda6689fbedc4b8"),
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- { // Amiga "Interplay" labeled version
- {
- "fw",
- "",
- AD_ENTRY1("part01", "a17a5eb15200c63276d486a88263ccd0"),
- Common::EN_USA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "3a87a913e0e33963a48a7f822ca0eb0e"),
- Common::DE_DEU,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- GF_ALT_FONT,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "5ad0007ccd5f7b3dd6b15ea7f281f9e1"),
- Common::ES_ESP,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "460f2da8793bc581a2d4b6fc19ccb5ae"),
- Common::FR_FRA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "1c8e5207743172134409ac58860021af"),
- Common::IT_ITA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "Demo",
- {
- { "demo", 0, "0f50767cd964e302d3af0ba2528df8c4", -1},
- { "demo.prc", 0, "d2ac3a743d288359c63644ea7071edae", -1},
- { NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "36050db13af57e462ca1adc4df99de4e"),
- Common::EN_ANY,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "ef245573b7dab0d4825ceb98e37cef4d"),
- Common::FR_FRA,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "os",
- "256 colors",
- AD_ENTRY1("procs00", "d6752e7d25924cb866b61eb7cb0c8b56"),
- Common::EN_GRB,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- // This is a 16 color PC version (It came on three 720kB 3.5" disks).
- // The protagonist is named John Glames in this version.
- {
- "os",
- "",
- AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"),
- Common::EN_GRB,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "d8c3a9d05a63e4cfa801826a7063a126"),
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "256 colors",
- AD_ENTRY1("procs00", "862a75d76fb7fffec30e52be9ad1c474"),
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- GF_CD,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "39b91ae35d1297ce0a76a1a803ca1593"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "74c2dabd9d212525fca8875a5f6d8994"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "256 colors",
- {
- { "procs1", 0, "74c2dabd9d212525fca8875a5f6d8994", -1},
- { "sds1", 0, "75443ba39cdc95667e07d7118e5c151c", -1},
- { NULL, 0, NULL, 0}
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- GF_CD,
- },
-
- {
- {
- "os",
- "256 colors",
- AD_ENTRY1("procs00", "f143567f08cfd1a9b1c9a41c89eadfef"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "da066e6b8dd93f2502c2a3755f08dc12"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "a9da5531ead0ebf9ad387fa588c0cbb0"),
- Common::EN_GRB,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "alt",
- AD_ENTRY1("procs0", "8a429ced2f4acff8a15ae125174042e8"),
- Common::EN_GRB,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "d5f27e33fc29c879f36f15b86ccfa58c"),
- Common::EN_USA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "8b7dce249821d3a62b314399c4334347"),
- Common::DE_DEU,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "35fc295ddd0af9da932d256ba799a4b0"),
- Common::ES_ESP,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "d4ea4a97e01fa67ea066f9e785050ed2"),
- Common::FR_FRA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "Demo",
- AD_ENTRY1("demo", "8d3a750d1c840b1b1071e42f9e6f6aa2"),
- Common::EN_GRB,
- Common::kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NONE
- },
- GType_OS,
- GF_DEMO,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "1501d5ae364b2814a33ed19347c3fcae"),
- Common::EN_GRB,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "2148d25de3219dd4a36580ca735d0afa"),
- Common::FR_FRA,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- { AD_TABLE_END_MARKER, 0, 0 }
-};
-
-} // End of namespace Cine
+#include "cine/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -569,7 +80,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI
+ Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class CineMetaEngine : public AdvancedMetaEngine {
@@ -736,7 +251,7 @@ Common::Error CineEngine::saveGameState(int slot, const char *desc) {
char indexFile[80];
snprintf(indexFile, 80, "%s.dir", _targetName.c_str());
- Common::OutSaveFile *fHandle = g_saveFileMan->openForSaving(indexFile);
+ Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(indexFile);
if (!fHandle) {
warning("Unable to open file %s for saving", indexFile);
return Common::kUnknownError;
diff --git a/engines/cine/detection_tables.h b/engines/cine/detection_tables.h
new file mode 100644
index 0000000000..6e450ebc80
--- /dev/null
+++ b/engines/cine/detection_tables.h
@@ -0,0 +1,513 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+namespace Cine {
+
+using Common::GUIO_NONE;
+
+static const CINEGameDescription gameDescriptions[] = {
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "61d003202d301c29dd399acfb1354310"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ // This is a CD version of Future Wars published by Sony.
+ // This version has a crypted AUTO00.PRC.
+ {
+ {
+ "fw",
+ "Sony CD version",
+ {
+ { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1},
+ { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_CD,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_CD | GF_CRYPTED_BOOT_PRC,
+ },
+
+ {
+ // This is the version included in the UK "Classic Collection"
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "91d7271155520eae6915a9dd2dac120c"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "f5e98fcca3fb5e7afa284c81c39d8b14"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_ALT_FONT,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "570109f965c7f53984b98c83d86eb206"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_ALT_FONT,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "5d1acb97abe9591f9008e00d07add95a"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "57afd280b598b4180fda6689fbedc4b8"),
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ { // Amiga "Interplay" labeled version
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "a17a5eb15200c63276d486a88263ccd0"),
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "3a87a913e0e33963a48a7f822ca0eb0e"),
+ Common::DE_DEU,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_ALT_FONT,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "5ad0007ccd5f7b3dd6b15ea7f281f9e1"),
+ Common::ES_ESP,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "460f2da8793bc581a2d4b6fc19ccb5ae"),
+ Common::FR_FRA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "1c8e5207743172134409ac58860021af"),
+ Common::IT_ITA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "Demo",
+ {
+ { "demo", 0, "0f50767cd964e302d3af0ba2528df8c4", -1},
+ { "demo.prc", 0, "d2ac3a743d288359c63644ea7071edae", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "36050db13af57e462ca1adc4df99de4e"),
+ Common::EN_ANY,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "ef245573b7dab0d4825ceb98e37cef4d"),
+ Common::FR_FRA,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ AD_ENTRY1("procs00", "d6752e7d25924cb866b61eb7cb0c8b56"),
+ Common::EN_GRB,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ // This is a 16 color PC version (It came on three 720kB 3.5" disks).
+ // The protagonist is named John Glames in this version.
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"),
+ Common::EN_GRB,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "d8c3a9d05a63e4cfa801826a7063a126"),
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ AD_ENTRY1("procs00", "862a75d76fb7fffec30e52be9ad1c474"),
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ GF_CD,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "39b91ae35d1297ce0a76a1a803ca1593"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "74c2dabd9d212525fca8875a5f6d8994"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ {
+ { "procs1", 0, "74c2dabd9d212525fca8875a5f6d8994", -1},
+ { "sds1", 0, "75443ba39cdc95667e07d7118e5c151c", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ GF_CD,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ AD_ENTRY1("procs00", "f143567f08cfd1a9b1c9a41c89eadfef"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "da066e6b8dd93f2502c2a3755f08dc12"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "a9da5531ead0ebf9ad387fa588c0cbb0"),
+ Common::EN_GRB,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "alt",
+ AD_ENTRY1("procs0", "8a429ced2f4acff8a15ae125174042e8"),
+ Common::EN_GRB,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "d5f27e33fc29c879f36f15b86ccfa58c"),
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "8b7dce249821d3a62b314399c4334347"),
+ Common::DE_DEU,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "35fc295ddd0af9da932d256ba799a4b0"),
+ Common::ES_ESP,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "d4ea4a97e01fa67ea066f9e785050ed2"),
+ Common::FR_FRA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "Demo",
+ AD_ENTRY1("demo", "8d3a750d1c840b1b1071e42f9e6f6aa2"),
+ Common::EN_GRB,
+ Common::kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GType_OS,
+ GF_DEMO,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "1501d5ae364b2814a33ed19347c3fcae"),
+ Common::EN_GRB,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "2148d25de3219dd4a36580ca735d0afa"),
+ Common::FR_FRA,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ { AD_TABLE_END_MARKER, 0, 0 }
+};
+
+} // End of namespace Cine
diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp
index a4220e6e35..4d61be102b 100644
--- a/engines/cine/gfx.cpp
+++ b/engines/cine/gfx.cpp
@@ -160,13 +160,13 @@ void FWRenderer::clear() {
* @param fillColor Sprite color
*/
void FWRenderer::fillSprite(const ObjectStruct &obj, uint8 color) {
- const byte *data = animDataTable[obj.frame].data();
+ const byte *data = g_cine->_animDataTable[obj.frame].data();
int x, y, width, height;
x = obj.x;
y = obj.y;
- width = animDataTable[obj.frame]._realWidth;
- height = animDataTable[obj.frame]._height;
+ width = g_cine->_animDataTable[obj.frame]._realWidth;
+ height = g_cine->_animDataTable[obj.frame]._height;
gfxFillSprite(data, width, height, _backBuffer, x, y, color);
}
@@ -177,13 +177,13 @@ void FWRenderer::fillSprite(const ObjectStruct &obj, uint8 color) {
* @param fillColor Sprite color
*/
void FWRenderer::incrustMask(const ObjectStruct &obj, uint8 color) {
- const byte *data = animDataTable[obj.frame].data();
+ const byte *data = g_cine->_animDataTable[obj.frame].data();
int x, y, width, height;
x = obj.x;
y = obj.y;
- width = animDataTable[obj.frame]._realWidth;
- height = animDataTable[obj.frame]._height;
+ width = g_cine->_animDataTable[obj.frame]._realWidth;
+ height = g_cine->_animDataTable[obj.frame]._height;
gfxFillSprite(data, width, height, _background, x, y, color);
}
@@ -194,13 +194,13 @@ void FWRenderer::incrustMask(const ObjectStruct &obj, uint8 color) {
* @param mask External mask
*/
void FWRenderer::drawMaskedSprite(const ObjectStruct &obj, const byte *mask) {
- const byte *data = animDataTable[obj.frame].data();
+ const byte *data = g_cine->_animDataTable[obj.frame].data();
int x, y, width, height;
x = obj.x;
y = obj.y;
- width = animDataTable[obj.frame]._realWidth;
- height = animDataTable[obj.frame]._height;
+ width = g_cine->_animDataTable[obj.frame]._realWidth;
+ height = g_cine->_animDataTable[obj.frame]._height;
assert(mask);
@@ -212,7 +212,7 @@ void FWRenderer::drawMaskedSprite(const ObjectStruct &obj, const byte *mask) {
* @param obj Object info
*/
void FWRenderer::drawSprite(const ObjectStruct &obj) {
- const byte *mask = animDataTable[obj.frame].mask();
+ const byte *mask = g_cine->_animDataTable[obj.frame].mask();
drawMaskedSprite(obj, mask);
}
@@ -221,14 +221,14 @@ void FWRenderer::drawSprite(const ObjectStruct &obj) {
* @param obj Object info
*/
void FWRenderer::incrustSprite(const ObjectStruct &obj) {
- const byte *data = animDataTable[obj.frame].data();
- const byte *mask = animDataTable[obj.frame].mask();
+ const byte *data = g_cine->_animDataTable[obj.frame].data();
+ const byte *mask = g_cine->_animDataTable[obj.frame].mask();
int x, y, width, height;
x = obj.x;
y = obj.y;
- width = animDataTable[obj.frame]._realWidth;
- height = animDataTable[obj.frame]._height;
+ width = g_cine->_animDataTable[obj.frame]._realWidth;
+ height = g_cine->_animDataTable[obj.frame]._height;
// There was an assert(mask) here before but it made savegame loading
// in Future Wars sometimes fail the assertion (e.g. see bug #2055912).
@@ -502,27 +502,27 @@ void FWRenderer::drawLine(int x, int y, int width, int height, byte color) {
* @param it Overlay info from overlayList
*/
void FWRenderer::remaskSprite(byte *mask, Common::List<overlay>::iterator it) {
- AnimData &sprite = animDataTable[objectTable[it->objIdx].frame];
+ AnimData &sprite = g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame];
int x, y, width, height, idx;
int mx, my, mw, mh;
- x = objectTable[it->objIdx].x;
- y = objectTable[it->objIdx].y;
+ x = g_cine->_objectTable[it->objIdx].x;
+ y = g_cine->_objectTable[it->objIdx].y;
width = sprite._realWidth;
height = sprite._height;
- for (++it; it != overlayList.end(); ++it) {
+ for (++it; it != g_cine->_overlayList.end(); ++it) {
if (it->type != 5) {
continue;
}
- idx = ABS(objectTable[it->objIdx].frame);
- mx = objectTable[it->objIdx].x;
- my = objectTable[it->objIdx].y;
- mw = animDataTable[idx]._realWidth;
- mh = animDataTable[idx]._height;
+ idx = ABS(g_cine->_objectTable[it->objIdx].frame);
+ mx = g_cine->_objectTable[it->objIdx].x;
+ my = g_cine->_objectTable[it->objIdx].y;
+ mw = g_cine->_animDataTable[idx]._realWidth;
+ mh = g_cine->_animDataTable[idx]._height;
- gfxUpdateSpriteMask(mask, x, y, width, height, animDataTable[idx].data(), mx, my, mw, mh);
+ gfxUpdateSpriteMask(mask, x, y, width, height, g_cine->_animDataTable[idx].data(), mx, my, mw, mh);
}
}
@@ -547,26 +547,26 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
switch (it->type) {
// color sprite
case 0:
- if (objectTable[it->objIdx].frame < 0) {
+ if (g_cine->_objectTable[it->objIdx].frame < 0) {
return;
}
- sprite = &animDataTable[objectTable[it->objIdx].frame];
+ sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame];
len = sprite->_realWidth * sprite->_height;
mask = new byte[len];
memcpy(mask, sprite->mask(), len);
remaskSprite(mask, it);
- drawMaskedSprite(objectTable[it->objIdx], mask);
+ drawMaskedSprite(g_cine->_objectTable[it->objIdx], mask);
delete[] mask;
break;
// game message
case 2:
- if (it->objIdx >= messageTable.size()) {
+ if (it->objIdx >= g_cine->_messageTable.size()) {
return;
}
- _messageLen += messageTable[it->objIdx].size();
- drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
+ _messageLen += g_cine->_messageTable[it->objIdx].size();
+ drawMessage(g_cine->_messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
waitForPlayerClick = 1;
break;
@@ -585,13 +585,13 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
// bitmap
case 4:
assert(it->objIdx < NUM_MAX_OBJECT);
- obj = &objectTable[it->objIdx];
+ obj = &g_cine->_objectTable[it->objIdx];
if (obj->frame < 0) {
return;
}
- if (!animDataTable[obj->frame].data()) {
+ if (!g_cine->_animDataTable[obj->frame].data()) {
return;
}
@@ -606,7 +606,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
void FWRenderer::drawOverlays() {
Common::List<overlay>::iterator it;
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
renderOverlay(it);
}
}
@@ -772,7 +772,7 @@ const char *FWRenderer::getBgName(uint idx) const {
* Restore active and backup palette from save
* @param fHandle Savefile open for reading
*/
-void FWRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
+void FWRenderer::restorePalette(Common::SeekableReadStream &fHandle, int version) {
byte buf[kLowPalNumBytes];
// Load the active 16 color palette from file
@@ -819,9 +819,8 @@ void FWRenderer::savePalette(Common::OutSaveFile &fHandle) {
void OSRenderer::savePalette(Common::OutSaveFile &fHandle) {
byte buf[kHighPalNumBytes];
- // Make sure the active palette has the correct format and color count
- assert(_activePal.colorFormat() == kHighPalFormat);
- assert(_activePal.colorCount() == kHighPalNumColors);
+ // We can have 16 color palette in many cases
+ fHandle.writeUint16LE(_activePal.colorCount());
// Write the active 256 color palette.
_activePal.save(buf, sizeof(buf), CINE_LITTLE_ENDIAN);
@@ -836,12 +835,18 @@ void OSRenderer::savePalette(Common::OutSaveFile &fHandle) {
* Restore active and backup palette from save
* @param fHandle Savefile open for reading
*/
-void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
+void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle, int version) {
byte buf[kHighPalNumBytes];
+ uint colorCount = (version > 0) ? fHandle.readUint16LE() : kHighPalNumBytes;
- // Load the active 256 color palette from file
fHandle.read(buf, kHighPalNumBytes);
- _activePal.load(buf, sizeof(buf), kHighPalFormat, kHighPalNumColors, CINE_LITTLE_ENDIAN);
+
+ if (colorCount == kHighPalNumBytes) {
+ // Load the active 256 color palette from file
+ _activePal.load(buf, sizeof(buf), kHighPalFormat, kHighPalNumColors, CINE_LITTLE_ENDIAN);
+ } else {
+ _activePal.load(buf, sizeof(buf), kLowPalFormat, kLowPalNumColors, CINE_LITTLE_ENDIAN);
+ }
// Jump over the backup 256 color palette.
// FIXME: Load the backup 256 color palette and use it properly.
@@ -1117,13 +1122,13 @@ void OSRenderer::clear() {
* @param fillColor Sprite color
*/
void OSRenderer::incrustMask(const ObjectStruct &obj, uint8 color) {
- const byte *data = animDataTable[obj.frame].data();
+ const byte *data = g_cine->_animDataTable[obj.frame].data();
int x, y, width, height;
x = obj.x;
y = obj.y;
- width = animDataTable[obj.frame]._realWidth;
- height = animDataTable[obj.frame]._height;
+ width = g_cine->_animDataTable[obj.frame]._realWidth;
+ height = g_cine->_animDataTable[obj.frame]._height;
if (_bgTable[_currentBg].bg) {
gfxFillSprite(data, width, height, _bgTable[_currentBg].bg, x, y, color);
@@ -1135,14 +1140,14 @@ void OSRenderer::incrustMask(const ObjectStruct &obj, uint8 color) {
* @param obj Object info
*/
void OSRenderer::drawSprite(const ObjectStruct &obj) {
- const byte *data = animDataTable[obj.frame].data();
+ const byte *data = g_cine->_animDataTable[obj.frame].data();
int x, y, width, height, transColor;
x = obj.x;
y = obj.y;
transColor = obj.part;
- width = animDataTable[obj.frame]._realWidth;
- height = animDataTable[obj.frame]._height;
+ width = g_cine->_animDataTable[obj.frame]._realWidth;
+ height = g_cine->_animDataTable[obj.frame]._height;
drawSpriteRaw2(data, transColor, width, height, _backBuffer, x, y);
}
@@ -1152,14 +1157,14 @@ void OSRenderer::drawSprite(const ObjectStruct &obj) {
* @param obj Object info
*/
void OSRenderer::incrustSprite(const ObjectStruct &obj) {
- const byte *data = animDataTable[obj.frame].data();
+ const byte *data = g_cine->_animDataTable[obj.frame].data();
int x, y, width, height, transColor;
x = obj.x;
y = obj.y;
transColor = obj.part;
- width = animDataTable[obj.frame]._realWidth;
- height = animDataTable[obj.frame]._height;
+ width = g_cine->_animDataTable[obj.frame]._realWidth;
+ height = g_cine->_animDataTable[obj.frame]._height;
if (_bgTable[_currentBg].bg) {
drawSpriteRaw2(data, transColor, width, height, _bgTable[_currentBg].bg, x, y);
@@ -1228,26 +1233,26 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
switch (it->type) {
// color sprite
case 0:
- if (objectTable[it->objIdx].frame < 0) {
+ if (g_cine->_objectTable[it->objIdx].frame < 0) {
break;
}
- sprite = &animDataTable[objectTable[it->objIdx].frame];
+ sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame];
len = sprite->_realWidth * sprite->_height;
mask = new byte[len];
- generateMask(sprite->data(), mask, len, objectTable[it->objIdx].part);
+ generateMask(sprite->data(), mask, len, g_cine->_objectTable[it->objIdx].part);
remaskSprite(mask, it);
- drawMaskedSprite(objectTable[it->objIdx], mask);
+ drawMaskedSprite(g_cine->_objectTable[it->objIdx], mask);
delete[] mask;
break;
// game message
case 2:
- if (it->objIdx >= messageTable.size()) {
+ if (it->objIdx >= g_cine->_messageTable.size()) {
return;
}
- _messageLen += messageTable[it->objIdx].size();
- drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
+ _messageLen += g_cine->_messageTable[it->objIdx].size();
+ drawMessage(g_cine->_messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
if (it->color >= 0) { // This test isn't in Future Wars's implementation
waitForPlayerClick = 1;
}
@@ -1268,7 +1273,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
// bitmap
case 4:
- if (objectTable[it->objIdx].frame >= 0) {
+ if (g_cine->_objectTable[it->objIdx].frame >= 0) {
FWRenderer::renderOverlay(it);
}
break;
@@ -1277,8 +1282,8 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
case 20:
assert(it->objIdx < NUM_MAX_OBJECT);
var5 = it->x; // A global variable updated here!
- obj = &objectTable[it->objIdx];
- sprite = &animDataTable[obj->frame];
+ obj = &g_cine->_objectTable[it->objIdx];
+ sprite = &g_cine->_animDataTable[obj->frame];
if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) {
break;
@@ -1298,7 +1303,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
case 22:
// TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing).
assert(it->objIdx < NUM_MAX_OBJECT);
- obj = &objectTable[it->objIdx];
+ obj = &g_cine->_objectTable[it->objIdx];
color = obj->part & 0x0F;
width = obj->frame;
height = obj->costume;
@@ -1792,17 +1797,17 @@ void maskBgOverlay(const byte *bgPtr, const byte *maskPtr, int16 width, int16 he
maskPtr = backup;
// incrust pass
- for (it = bgIncrustList.begin(); it != bgIncrustList.end(); ++it) {
- tmpWidth = animDataTable[it->frame]._realWidth;
- tmpHeight = animDataTable[it->frame]._height;
+ for (it = g_cine->_bgIncrustList.begin(); it != g_cine->_bgIncrustList.end(); ++it) {
+ tmpWidth = g_cine->_animDataTable[it->frame]._realWidth;
+ tmpHeight = g_cine->_animDataTable[it->frame]._height;
mask = (byte*)malloc(tmpWidth * tmpHeight);
if (it->param == 0) {
- generateMask(animDataTable[it->frame].data(), mask, tmpWidth * tmpHeight, it->part);
+ generateMask(g_cine->_animDataTable[it->frame].data(), mask, tmpWidth * tmpHeight, it->part);
gfxUpdateIncrustMask(mask, it->x, it->y, tmpWidth, tmpHeight, maskPtr, x, y, width, height);
- gfxDrawMaskedSprite(animDataTable[it->frame].data(), mask, tmpWidth, tmpHeight, page, it->x, it->y);
+ gfxDrawMaskedSprite(g_cine->_animDataTable[it->frame].data(), mask, tmpWidth, tmpHeight, page, it->x, it->y);
} else {
- memcpy(mask, animDataTable[it->frame].data(), tmpWidth * tmpHeight);
+ memcpy(mask, g_cine->_animDataTable[it->frame].data(), tmpWidth * tmpHeight);
gfxUpdateIncrustMask(mask, it->x, it->y, tmpWidth, tmpHeight, maskPtr, x, y, width, height);
gfxFillSprite(mask, tmpWidth, tmpHeight, page, it->x, it->y);
}
diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h
index 56ba6885f4..da7e3dd572 100644
--- a/engines/cine/gfx.h
+++ b/engines/cine/gfx.h
@@ -197,7 +197,7 @@ public:
virtual void refreshPalette();
virtual void reloadPalette();
- virtual void restorePalette(Common::SeekableReadStream &fHandle);
+ virtual void restorePalette(Common::SeekableReadStream &fHandle, int version);
virtual void savePalette(Common::OutSaveFile &fHandle);
virtual void rotatePalette(int a, int b, int c);
virtual void transformPalette(int first, int last, int r, int g, int b);
@@ -257,7 +257,7 @@ public:
const char *getBgName(uint idx = 0) const;
void reloadPalette();
- void restorePalette(Common::SeekableReadStream &fHandle);
+ void restorePalette(Common::SeekableReadStream &fHandle, int version);
void savePalette(Common::OutSaveFile &fHandle);
void transformPalette(int first, int last, int r, int g, int b);
diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp
index 414aed394c..3d280c20ef 100644
--- a/engines/cine/main_loop.cpp
+++ b/engines/cine/main_loop.cpp
@@ -241,11 +241,11 @@ int getKeyData() {
/** Removes elements from seqList that have their member variable var4 set to value -1. */
void purgeSeqList() {
- Common::List<SeqListElement>::iterator it = seqList.begin();
- while (it != seqList.end()) {
+ Common::List<SeqListElement>::iterator it = g_cine->_seqList.begin();
+ while (it != g_cine->_seqList.end()) {
if (it->var4 == -1) {
// Erase the element and jump to the next element
- it = seqList.erase(it);
+ it = g_cine->_seqList.erase(it);
} else {
// Let the element be and jump to the next element
it++;
@@ -283,15 +283,15 @@ void CineEngine::mainLoop(int bootScriptIdx) {
menuCommandLen = 0;
playerCommand = -1;
- commandBuffer = "";
+ g_cine->_commandBuffer = "";
- globalVars[VAR_MOUSE_X_POS] = 0;
- globalVars[VAR_MOUSE_Y_POS] = 0;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = 0;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = 0;
if (g_cine->getGameType() == Cine::GType_OS) {
- globalVars[VAR_MOUSE_X_POS_2ND] = 0;
- globalVars[VAR_MOUSE_Y_POS_2ND] = 0;
- globalVars[VAR_BYPASS_PROTECTION] = 0; // set to 1 to bypass the copy protection
- globalVars[VAR_LOW_MEMORY] = 0; // set to 1 to disable some animations, sounds etc.
+ g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = 0;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = 0;
+ g_cine->_globalVars[VAR_BYPASS_PROTECTION] = 0; // set to 1 to bypass the copy protection
+ g_cine->_globalVars[VAR_LOW_MEMORY] = 0; // set to 1 to disable some animations, sounds etc.
}
strcpy(newPrcName, "");
@@ -315,7 +315,7 @@ void CineEngine::mainLoop(int bootScriptIdx) {
if (bgName == "28.PI1" || bgName == "29.PI1" || bgName == "30.PI1") {
static const uint oxygenObjNum = 202, maxOxygen = 264;
// Force the amount of oxygen left to the maximum.
- objectTable[oxygenObjNum].x = maxOxygen;
+ g_cine->_objectTable[oxygenObjNum].x = maxOxygen;
}
}
@@ -332,8 +332,8 @@ void CineEngine::mainLoop(int bootScriptIdx) {
// flower shop scene is AIRPORT.PRC's 13th script.
// FIXME: Remove the hack and solve what's really causing the problem in the first place.
if (g_cine->getGameType() == Cine::GType_OS) {
- if (scumm_stricmp(renderer->getBgName(), "21.PI1") == 0 && objectTable[1].x == 204 && objectTable[1].y == 110) {
- objectTable[1].y--; // Move the player character upward on-screen by one pixel
+ if (scumm_stricmp(renderer->getBgName(), "21.PI1") == 0 && g_cine->_objectTable[1].x == 204 && g_cine->_objectTable[1].y == 110) {
+ g_cine->_objectTable[1].y--; // Move the player character upward on-screen by one pixel
}
}
@@ -342,7 +342,7 @@ void CineEngine::mainLoop(int bootScriptIdx) {
// Clear the zoneQuery table (Operation Stealth specific)
if (g_cine->getGameType() == Cine::GType_OS) {
- Common::set_to(zoneQuery.begin(), zoneQuery.end(), 0);
+ Common::set_to(g_cine->_zoneQuery.begin(), g_cine->_zoneQuery.end(), 0);
}
if (g_cine->getGameType() == Cine::GType_OS) {
diff --git a/engines/cine/msg.cpp b/engines/cine/msg.cpp
index 45f81f7d05..a01afd147b 100644
--- a/engines/cine/msg.cpp
+++ b/engines/cine/msg.cpp
@@ -31,15 +31,11 @@
namespace Cine {
-// FIXME: Global C++ objects affect portability negatively.
-// Turn this into a class member instead.
-Common::StringArray messageTable;
-
void loadMsg(char *pMsgName) {
uint32 sourceSize;
checkDataDisk(-1);
- messageTable.clear();
+ g_cine->_messageTable.clear();
byte *dataPtr = readBundleFile(findFileInBundle(pMsgName), &sourceSize);
setMouseCursor(MOUSE_CURSOR_DISK);
@@ -58,7 +54,7 @@ void loadMsg(char *pMsgName) {
// This code works around input data that has empty strings residing outside the input
// buffer (e.g. message indexes 58-254 in BATEAU.MSG in PROCS08 in Operation Stealth).
if (messageDataPos < sourceSize) {
- messageTable.push_back((const char *)(dataPtr + messageDataPos));
+ g_cine->_messageTable.push_back((const char *)(dataPtr + messageDataPos));
} else {
if (messageLen > 0) { // Only warn about overflowing non-empty strings
warning("loadMsg(%s): message (%d. / %d) is overflowing the input buffer. Replacing it with an empty string", pMsgName, i + 1, count);
@@ -66,7 +62,7 @@ void loadMsg(char *pMsgName) {
debugC(5, kCineDebugPart, "loadMsg(%s): empty message (%d. / %d) resides outside input buffer", pMsgName, i + 1, count);
}
// Message resides outside the input buffer so we replace it with an empty string
- messageTable.push_back("");
+ g_cine->_messageTable.push_back("");
}
// Jump to the next message
messageDataPos += messageLen;
diff --git a/engines/cine/msg.h b/engines/cine/msg.h
index cf51cdb48f..fbf99d4b44 100644
--- a/engines/cine/msg.h
+++ b/engines/cine/msg.h
@@ -32,8 +32,6 @@ namespace Cine {
#define NUM_MAX_MESSAGE 255
-extern Common::StringArray messageTable;
-
void loadMsg(char *pMsgName);
} // End of namespace Cine
diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp
index 116b57c267..82dc0a6ef1 100644
--- a/engines/cine/object.cpp
+++ b/engines/cine/object.cpp
@@ -35,12 +35,9 @@
namespace Cine {
-Common::Array<ObjectStruct> objectTable;
-Common::List<overlay> overlayList;
-
/** Resets all elements in the object table. */
void resetObjectTable() {
- for (Common::Array<ObjectStruct>::iterator it = objectTable.begin(); it != objectTable.end(); ++it) {
+ for (Common::Array<ObjectStruct>::iterator it = g_cine->_objectTable.begin(); it != g_cine->_objectTable.end(); ++it) {
it->clear();
}
}
@@ -64,23 +61,23 @@ void loadObject(char *pObjectName) {
assert(numEntry <= NUM_MAX_OBJECT);
for (i = 0; i < numEntry; i++) {
- if (objectTable[i].costume != -2) { // flag is keep ?
+ if (g_cine->_objectTable[i].costume != -2) { // flag is keep ?
Common::MemoryReadStream readS(ptr, entrySize);
- objectTable[i].x = readS.readSint16BE();
- objectTable[i].y = readS.readSint16BE();
- objectTable[i].mask = readS.readUint16BE();
- objectTable[i].frame = readS.readSint16BE();
- objectTable[i].costume = readS.readSint16BE();
- readS.read(objectTable[i].name, 20);
- objectTable[i].part = readS.readUint16BE();
+ g_cine->_objectTable[i].x = readS.readSint16BE();
+ g_cine->_objectTable[i].y = readS.readSint16BE();
+ g_cine->_objectTable[i].mask = readS.readUint16BE();
+ g_cine->_objectTable[i].frame = readS.readSint16BE();
+ g_cine->_objectTable[i].costume = readS.readSint16BE();
+ readS.read(g_cine->_objectTable[i].name, 20);
+ g_cine->_objectTable[i].part = readS.readUint16BE();
}
ptr += entrySize;
}
if (!strcmp(pObjectName, "INTRO.OBJ")) {
for (i = 0; i < 10; i++) {
- objectTable[i].costume = 0;
+ g_cine->_objectTable[i].costume = 0;
}
}
@@ -95,9 +92,9 @@ void loadObject(char *pObjectName) {
int removeOverlay(uint16 objIdx, uint16 param) {
Common::List<overlay>::iterator it;
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
if (it->objIdx == objIdx && it->type == param) {
- overlayList.erase(it);
+ g_cine->_overlayList.erase(it);
return 1;
}
}
@@ -115,9 +112,9 @@ void addOverlay(uint16 objIdx, uint16 type) {
Common::List<overlay>::iterator it;
overlay tmp;
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
// This is done for both Future Wars and Operation Stealth
- if (objectTable[it->objIdx].mask >= objectTable[objIdx].mask) {
+ if (g_cine->_objectTable[it->objIdx].mask >= g_cine->_objectTable[objIdx].mask) {
break;
}
@@ -128,7 +125,7 @@ void addOverlay(uint16 objIdx, uint16 type) {
}
// In Operation Stealth's implementation we might bail out early
- if (g_cine->getGameType() == Cine::GType_OS && it != overlayList.end() && it->objIdx == objIdx && it->type == type) {
+ if (g_cine->getGameType() == Cine::GType_OS && it != g_cine->_overlayList.end() && it->objIdx == objIdx && it->type == type) {
return;
}
@@ -139,7 +136,7 @@ void addOverlay(uint16 objIdx, uint16 type) {
tmp.width = 0;
tmp.color = 0;
- overlayList.insert(it, tmp);
+ g_cine->_overlayList.insert(it, tmp);
}
/**
@@ -151,13 +148,13 @@ void addGfxElement(int16 objIdx, int16 param, int16 type) {
Common::List<overlay>::iterator it;
overlay tmp;
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
- if (objectTable[it->objIdx].mask >= objectTable[objIdx].mask || it->type == 2 || it->type == 3) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
+ if (g_cine->_objectTable[it->objIdx].mask >= g_cine->_objectTable[objIdx].mask || it->type == 2 || it->type == 3) {
break;
}
}
- if (it != overlayList.end() && it->objIdx == objIdx && it->type == type && it->x == param) {
+ if (it != g_cine->_overlayList.end() && it->objIdx == objIdx && it->type == type && it->x == param) {
return;
}
@@ -168,7 +165,7 @@ void addGfxElement(int16 objIdx, int16 param, int16 type) {
tmp.width = 0;
tmp.color = 0;
- overlayList.insert(it, tmp);
+ g_cine->_overlayList.insert(it, tmp);
}
/**
@@ -180,19 +177,19 @@ void addGfxElement(int16 objIdx, int16 param, int16 type) {
void removeGfxElement(int16 objIdx, int16 param, int16 type) {
Common::List<overlay>::iterator it;
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
if (it->objIdx == objIdx && it->type == type && it->x == param) {
- overlayList.erase(it);
+ g_cine->_overlayList.erase(it);
return;
}
}
}
void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4) {
- objectTable[objIdx].x = param1;
- objectTable[objIdx].y = param2;
- objectTable[objIdx].mask = param3;
- objectTable[objIdx].frame = param4;
+ g_cine->_objectTable[objIdx].x = param1;
+ g_cine->_objectTable[objIdx].y = param2;
+ g_cine->_objectTable[objIdx].mask = param3;
+ g_cine->_objectTable[objIdx].frame = param4;
if (g_cine->getGameType() == Cine::GType_OS) {
resetGfxEntityEntry(objIdx);
@@ -219,13 +216,13 @@ void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
switch (paramIdx) {
case 1:
- objectTable[objIdx].x = newValue;
+ g_cine->_objectTable[objIdx].x = newValue;
break;
case 2:
- objectTable[objIdx].y = newValue;
+ g_cine->_objectTable[objIdx].y = newValue;
break;
case 3:
- objectTable[objIdx].mask = newValue;
+ g_cine->_objectTable[objIdx].mask = newValue;
if (g_cine->getGameType() == Cine::GType_OS) { // Operation Stealth specific
resetGfxEntityEntry(objIdx);
@@ -236,18 +233,18 @@ void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
}
break;
case 4:
- objectTable[objIdx].frame = newValue;
+ g_cine->_objectTable[objIdx].frame = newValue;
break;
case 5:
// TODO: Test if this really breaks the newspaper machine on the airport in Operation Stealth.
if (g_cine->getGameType() == Cine::GType_FW && newValue == -1) {
- objectTable[objIdx].costume = globalVars[0];
+ g_cine->_objectTable[objIdx].costume = g_cine->_globalVars[0];
} else {
- objectTable[objIdx].costume = newValue;
+ g_cine->_objectTable[objIdx].costume = newValue;
}
break;
case 6:
- objectTable[objIdx].part = newValue;
+ g_cine->_objectTable[objIdx].part = newValue;
break;
}
}
@@ -263,8 +260,8 @@ bool compareRanges(uint16 aStart, uint16 aEnd, uint16 bStart, uint16 bEnd) {
uint16 compareObjectParamRanges(uint16 objIdx1, uint16 xAdd1, uint16 yAdd1, uint16 maskAdd1, uint16 objIdx2, uint16 xAdd2, uint16 yAdd2, uint16 maskAdd2) {
assert(objIdx1 < NUM_MAX_OBJECT && objIdx2 < NUM_MAX_OBJECT);
- const ObjectStruct &obj1 = objectTable[objIdx1];
- const ObjectStruct &obj2 = objectTable[objIdx2];
+ const ObjectStruct &obj1 = g_cine->_objectTable[objIdx1];
+ const ObjectStruct &obj2 = g_cine->_objectTable[objIdx2];
if (compareRanges(obj1.x, obj1.x + xAdd1, obj2.x, obj2.x + xAdd2) &&
compareRanges(obj1.y, obj1.y + yAdd1, obj2.y, obj2.y + yAdd2) &&
@@ -304,17 +301,17 @@ int16 getObjectParam(uint16 objIdx, uint16 paramIdx) {
switch (paramIdx) {
case 0:
- return objectTable[objIdx].x;
+ return g_cine->_objectTable[objIdx].x;
case 1:
- return objectTable[objIdx].y;
+ return g_cine->_objectTable[objIdx].y;
case 2:
- return objectTable[objIdx].mask;
+ return g_cine->_objectTable[objIdx].mask;
case 3:
- return objectTable[objIdx].frame;
+ return g_cine->_objectTable[objIdx].frame;
case 4:
- return objectTable[objIdx].costume;
+ return g_cine->_objectTable[objIdx].costume;
case 5:
- return objectTable[objIdx].part;
+ return g_cine->_objectTable[objIdx].part;
}
return 0;
diff --git a/engines/cine/object.h b/engines/cine/object.h
index daf515bf0f..5a5ea91286 100644
--- a/engines/cine/object.h
+++ b/engines/cine/object.h
@@ -63,9 +63,6 @@ struct overlay {
#define NUM_MAX_OBJECT 255
#define NUM_MAX_VAR 255
-extern Common::Array<ObjectStruct> objectTable;
-extern Common::List<overlay> overlayList;
-
void resetObjectTable();
void loadObject(char *pObjectName);
void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4);
diff --git a/engines/cine/pal.cpp b/engines/cine/pal.cpp
index 6e730aedd9..27d0e593da 100644
--- a/engines/cine/pal.cpp
+++ b/engines/cine/pal.cpp
@@ -30,7 +30,6 @@
namespace Cine {
-Common::Array<PalEntry> palArray;
static byte paletteBuffer1[16];
static byte paletteBuffer2[16];
@@ -40,7 +39,7 @@ void loadPal(const char *fileName) {
removeExtention(buffer, fileName);
strcat(buffer, ".PAL");
- palArray.clear();
+ g_cine->_palArray.clear();
Common::File palFileHandle;
if (!palFileHandle.open(buffer))
@@ -49,11 +48,11 @@ void loadPal(const char *fileName) {
uint16 palEntriesCount = palFileHandle.readUint16LE();
palFileHandle.readUint16LE(); // entry size
- palArray.resize(palEntriesCount);
- for (uint i = 0; i < palArray.size(); ++i) {
- palFileHandle.read(palArray[i].name, 10);
- palFileHandle.read(palArray[i].pal1, 16);
- palFileHandle.read(palArray[i].pal2, 16);
+ g_cine->_palArray.resize(palEntriesCount);
+ for (uint i = 0; i < g_cine->_palArray.size(); ++i) {
+ palFileHandle.read(g_cine->_palArray[i].name, 10);
+ palFileHandle.read(g_cine->_palArray[i].pal1, 16);
+ palFileHandle.read(g_cine->_palArray[i].pal2, 16);
}
palFileHandle.close();
}
@@ -73,8 +72,8 @@ int16 findPaletteFromName(const char *fileName) {
position++;
}
- for (i = 0; i < palArray.size(); i++) {
- if (!strcmp(buffer, palArray[i].name)) {
+ for (i = 0; i < g_cine->_palArray.size(); i++) {
+ if (!strcmp(buffer, g_cine->_palArray[i].name)) {
return i;
}
}
@@ -97,9 +96,9 @@ void loadRelatedPalette(const char *fileName) {
paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i;
}
} else {
- assert(paletteIndex < (int32)palArray.size());
- memcpy(paletteBuffer1, palArray[paletteIndex].pal1, 16);
- memcpy(paletteBuffer2, palArray[paletteIndex].pal2, 16);
+ assert(paletteIndex < (int32)g_cine->_palArray.size());
+ memcpy(paletteBuffer1, g_cine->_palArray[paletteIndex].pal1, 16);
+ memcpy(paletteBuffer2, g_cine->_palArray[paletteIndex].pal2, 16);
}
}
diff --git a/engines/cine/pal.h b/engines/cine/pal.h
index 0086711636..e5b318b24d 100644
--- a/engines/cine/pal.h
+++ b/engines/cine/pal.h
@@ -49,8 +49,6 @@ struct PalEntry {
byte pal2[16];
};
-extern Common::Array<PalEntry> palArray;
-
void loadPal(const char *fileName);
void loadRelatedPalette(const char *fileName);
diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp
index ad5aaf54b0..95f3789abd 100644
--- a/engines/cine/part.cpp
+++ b/engines/cine/part.cpp
@@ -32,10 +32,8 @@
namespace Cine {
-Common::Array<PartBuffer> partBuffer;
-
void loadPart(const char *partName) {
- partBuffer.clear();
+ g_cine->_partBuffer.clear();
g_cine->_partFileHandle.close();
@@ -47,17 +45,17 @@ void loadPart(const char *partName) {
setMouseCursor(MOUSE_CURSOR_DISK);
uint16 numElementInPart = g_cine->_partFileHandle.readUint16BE();
- partBuffer.resize(numElementInPart);
+ g_cine->_partBuffer.resize(numElementInPart);
g_cine->_partFileHandle.readUint16BE(); // entry size
if (currentPartName != partName)
strcpy(currentPartName, partName);
- for (uint16 i = 0; i < partBuffer.size(); i++) {
- g_cine->_partFileHandle.read(partBuffer[i].partName, 14);
- partBuffer[i].offset = g_cine->_partFileHandle.readUint32BE();
- partBuffer[i].packedSize = g_cine->_partFileHandle.readUint32BE();
- partBuffer[i].unpackedSize = g_cine->_partFileHandle.readUint32BE();
+ for (uint16 i = 0; i < g_cine->_partBuffer.size(); i++) {
+ g_cine->_partFileHandle.read(g_cine->_partBuffer[i].partName, 14);
+ g_cine->_partBuffer[i].offset = g_cine->_partFileHandle.readUint32BE();
+ g_cine->_partBuffer[i].packedSize = g_cine->_partFileHandle.readUint32BE();
+ g_cine->_partBuffer[i].unpackedSize = g_cine->_partFileHandle.readUint32BE();
g_cine->_partFileHandle.readUint32BE(); // unused
}
@@ -189,8 +187,8 @@ void CineEngine::readVolCnf() {
int16 findFileInBundle(const char *fileName) {
if (g_cine->getGameType() == Cine::GType_OS) {
// look first in currently loaded resource file
- for (uint i = 0; i < partBuffer.size(); i++) {
- if (!scumm_stricmp(fileName, partBuffer[i].partName)) {
+ for (uint i = 0; i < g_cine->_partBuffer.size(); i++) {
+ if (!scumm_stricmp(fileName, g_cine->_partBuffer[i].partName)) {
return i;
}
}
@@ -203,8 +201,8 @@ int16 findFileInBundle(const char *fileName) {
const char *part = (*it)._value;
loadPart(part);
}
- for (uint i = 0; i < partBuffer.size(); i++) {
- if (!scumm_stricmp(fileName, partBuffer[i].partName)) {
+ for (uint i = 0; i < g_cine->_partBuffer.size(); i++) {
+ if (!scumm_stricmp(fileName, g_cine->_partBuffer[i].partName)) {
return i;
}
}
@@ -212,31 +210,31 @@ int16 findFileInBundle(const char *fileName) {
}
void readFromPart(int16 idx, byte *dataPtr, uint32 maxSize) {
- assert(maxSize >= partBuffer[idx].packedSize);
+ assert(maxSize >= g_cine->_partBuffer[idx].packedSize);
setMouseCursor(MOUSE_CURSOR_DISK);
- g_cine->_partFileHandle.seek(partBuffer[idx].offset, SEEK_SET);
- g_cine->_partFileHandle.read(dataPtr, partBuffer[idx].packedSize);
+ g_cine->_partFileHandle.seek(g_cine->_partBuffer[idx].offset, SEEK_SET);
+ g_cine->_partFileHandle.read(dataPtr, g_cine->_partBuffer[idx].packedSize);
}
byte *readBundleFile(int16 foundFileIdx, uint32 *size) {
- assert(foundFileIdx >= 0 && foundFileIdx < (int32)partBuffer.size());
+ assert(foundFileIdx >= 0 && foundFileIdx < (int32)g_cine->_partBuffer.size());
bool error = false;
- byte *dataPtr = (byte *)calloc(partBuffer[foundFileIdx].unpackedSize, 1);
- byte *packedData = (byte *)calloc(partBuffer[foundFileIdx].packedSize, 1);
+ byte *dataPtr = (byte *)calloc(g_cine->_partBuffer[foundFileIdx].unpackedSize, 1);
+ byte *packedData = (byte *)calloc(g_cine->_partBuffer[foundFileIdx].packedSize, 1);
assert(dataPtr && packedData);
- readFromPart(foundFileIdx, packedData, partBuffer[foundFileIdx].packedSize);
+ readFromPart(foundFileIdx, packedData, g_cine->_partBuffer[foundFileIdx].packedSize);
CineUnpacker cineUnpacker;
- error = !cineUnpacker.unpack(packedData, partBuffer[foundFileIdx].packedSize, dataPtr, partBuffer[foundFileIdx].unpackedSize);
+ error = !cineUnpacker.unpack(packedData, g_cine->_partBuffer[foundFileIdx].packedSize, dataPtr, g_cine->_partBuffer[foundFileIdx].unpackedSize);
free(packedData);
if (error) {
- warning("Error unpacking '%s' from bundle file '%s'", partBuffer[foundFileIdx].partName, currentPartName);
+ warning("Error unpacking '%s' from bundle file '%s'", g_cine->_partBuffer[foundFileIdx].partName, currentPartName);
}
// Set the size variable if a pointer to it has been given
if (size != NULL) {
- *size = partBuffer[foundFileIdx].unpackedSize;
+ *size = g_cine->_partBuffer[foundFileIdx].unpackedSize;
}
return dataPtr;
@@ -255,7 +253,7 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) {
if (index != -1) {
data = readBundleFile(index);
if (size) {
- *size = partBuffer[index].unpackedSize;
+ *size = g_cine->_partBuffer[index].unpackedSize;
}
}
if (g_cine->getGameType() == Cine::GType_FW) {
@@ -305,14 +303,14 @@ void dumpBundle(const char *fileName) {
strcpy(tmpPart, currentPartName);
loadPart(fileName);
- for (uint i = 0; i < partBuffer.size(); i++) {
+ for (uint i = 0; i < g_cine->_partBuffer.size(); i++) {
byte *data = readBundleFile(i);
- debug(0, "%s", partBuffer[i].partName);
+ debug(0, "%s", g_cine->_partBuffer[i].partName);
Common::DumpFile out;
- if (out.open(Common::String("dumps/") + partBuffer[i].partName)) {
- out.write(data, partBuffer[i].unpackedSize);
+ if (out.open(Common::String("dumps/") + g_cine->_partBuffer[i].partName)) {
+ out.write(data, g_cine->_partBuffer[i].unpackedSize);
out.close();
}
diff --git a/engines/cine/part.h b/engines/cine/part.h
index 2f1af7141f..53ebafaa37 100644
--- a/engines/cine/part.h
+++ b/engines/cine/part.h
@@ -37,8 +37,6 @@ struct PartBuffer {
#define NUM_MAX_PARTDATA 255
-extern Common::Array<PartBuffer> partBuffer;
-
void loadPart(const char *partName);
void closePart();
diff --git a/engines/cine/prc.cpp b/engines/cine/prc.cpp
index e1ef14bbbf..183a537416 100644
--- a/engines/cine/prc.cpp
+++ b/engines/cine/prc.cpp
@@ -35,9 +35,6 @@
namespace Cine {
-ScriptList globalScripts;
-ScriptList objectScripts;
-
//char currentPrcName[20];
/**
@@ -52,8 +49,8 @@ bool loadPrc(const char *pPrcName) {
assert(pPrcName);
- globalScripts.clear();
- scriptTable.clear();
+ g_cine->_globalScripts.clear();
+ g_cine->_scriptTable.clear();
// This is copy protection. Used to hang the machine
if (!scumm_stricmp(pPrcName, COPY_PROT_FAIL_PRC_NAME)) {
@@ -83,14 +80,14 @@ bool loadPrc(const char *pPrcName) {
RawScriptPtr tmp(new RawScript(READ_BE_UINT16(scriptPtr)));
scriptPtr += 2;
assert(tmp);
- scriptTable.push_back(tmp);
+ g_cine->_scriptTable.push_back(tmp);
}
for (i = 0; i < numScripts; i++) {
- uint16 size = scriptTable[i]->_size;
+ uint16 size = g_cine->_scriptTable[i]->_size;
// TODO: delete the test?
if (size) {
- scriptTable[i]->setData(*scriptInfo, scriptPtr);
+ g_cine->_scriptTable[i]->setData(*scriptInfo, scriptPtr);
scriptPtr += size;
}
}
diff --git a/engines/cine/prc.h b/engines/cine/prc.h
index 05bb240372..84058426fa 100644
--- a/engines/cine/prc.h
+++ b/engines/cine/prc.h
@@ -28,9 +28,6 @@
namespace Cine {
-extern ScriptList globalScripts;
-extern ScriptList objectScripts;
-
bool loadPrc(const char *pPrcName);
} // End of namespace Cine
diff --git a/engines/cine/rel.cpp b/engines/cine/rel.cpp
index 17ab14bfe5..4a11995ee1 100644
--- a/engines/cine/rel.cpp
+++ b/engines/cine/rel.cpp
@@ -31,8 +31,6 @@
namespace Cine {
-RawObjectScriptArray relTable; ///< Object script bytecode table
-
/**
* @todo Is script size of 0 valid?
* @todo Fix script dump code
@@ -45,8 +43,8 @@ void loadRel(char *pRelName) {
checkDataDisk(-1);
- objectScripts.clear();
- relTable.clear();
+ g_cine->_objectScripts.clear();
+ g_cine->_relTable.clear();
ptr = dataPtr = readBundleFile(findFileInBundle(pRelName));
@@ -61,14 +59,14 @@ void loadRel(char *pRelName) {
p3 = READ_BE_UINT16(ptr); ptr += 2;
RawObjectScriptPtr tmp(new RawObjectScript(size, p1, p2, p3));
assert(tmp);
- relTable.push_back(tmp);
+ g_cine->_relTable.push_back(tmp);
}
for (i = 0; i < numEntry; i++) {
- size = relTable[i]->_size;
+ size = g_cine->_relTable[i]->_size;
// TODO: delete the test?
if (size) {
- relTable[i]->setData(*scriptInfo, ptr);
+ g_cine->_relTable[i]->setData(*scriptInfo, ptr);
ptr += size;
}
}
@@ -82,10 +80,10 @@ void loadRel(char *pRelName) {
char buffer[256];
for (s = 0; s < numEntry; s++) {
- if (relTable[s]->_size) {
+ if (g_cine->_relTable[s]->_size) {
sprintf(buffer, "%s_%03d.txt", pRelName, s);
- decompileScript((const byte *)relTable[s]->getString(0), relTable[s]->_size, s);
+ decompileScript((const byte *)g_cine->_relTable[s]->getString(0), g_cine->_relTable[s]->_size, s);
dumpScript(buffer);
}
}
diff --git a/engines/cine/rel.h b/engines/cine/rel.h
index 84926f76c4..8d712dc416 100644
--- a/engines/cine/rel.h
+++ b/engines/cine/rel.h
@@ -29,8 +29,6 @@
#include "cine/script.h"
namespace Cine {
-extern RawObjectScriptArray relTable;
-
void loadRel(char *pRelName);
} // End of namespace Cine
diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp
index c76bed3f8e..0f7c50b7e5 100644
--- a/engines/cine/saveload.cpp
+++ b/engines/cine/saveload.cpp
@@ -200,13 +200,13 @@ void loadScriptFromSave(Common::SeekableReadStream &fHandle, bool isGlobal) {
// original code loaded everything into globalScripts, this should be
// the correct behavior
if (isGlobal) {
- ScriptPtr tmp(scriptInfo->create(*scriptTable[idx], idx, labels, localVars, compare, pos));
+ ScriptPtr tmp(scriptInfo->create(*g_cine->_scriptTable[idx], idx, labels, localVars, compare, pos));
assert(tmp);
- globalScripts.push_back(tmp);
+ g_cine->_globalScripts.push_back(tmp);
} else {
- ScriptPtr tmp(scriptInfo->create(*relTable[idx], idx, labels, localVars, compare, pos));
+ ScriptPtr tmp(scriptInfo->create(*g_cine->_relTable[idx], idx, labels, localVars, compare, pos));
assert(tmp);
- objectScripts.push_back(tmp);
+ g_cine->_objectScripts.push_back(tmp);
}
}
@@ -227,7 +227,7 @@ void loadOverlayFromSave(Common::SeekableReadStream &fHandle) {
tmp.width = fHandle.readSint16BE();
tmp.color = fHandle.readSint16BE();
- overlayList.push_back(tmp);
+ g_cine->_overlayList.push_back(tmp);
}
bool loadObjectTable(Common::SeekableReadStream &in) {
@@ -235,20 +235,20 @@ bool loadObjectTable(Common::SeekableReadStream &in) {
in.readUint16BE(); // Entry size
for (int i = 0; i < NUM_MAX_OBJECT; i++) {
- objectTable[i].x = in.readSint16BE();
- objectTable[i].y = in.readSint16BE();
- objectTable[i].mask = in.readUint16BE();
- objectTable[i].frame = in.readSint16BE();
- objectTable[i].costume = in.readSint16BE();
- in.read(objectTable[i].name, 20);
- objectTable[i].part = in.readUint16BE();
+ g_cine->_objectTable[i].x = in.readSint16BE();
+ g_cine->_objectTable[i].y = in.readSint16BE();
+ g_cine->_objectTable[i].mask = in.readUint16BE();
+ g_cine->_objectTable[i].frame = in.readSint16BE();
+ g_cine->_objectTable[i].costume = in.readSint16BE();
+ in.read(g_cine->_objectTable[i].name, 20);
+ g_cine->_objectTable[i].part = in.readUint16BE();
}
return !(in.eos() || in.err());
}
bool loadZoneData(Common::SeekableReadStream &in) {
for (int i = 0; i < 16; i++) {
- zoneData[i] = in.readUint16BE();
+ g_cine->_zoneData[i] = in.readUint16BE();
}
return !(in.eos() || in.err());
}
@@ -313,14 +313,14 @@ bool loadSeqList(Common::SeekableReadStream &in) {
tmp.var1A = in.readSint16BE();
tmp.var1C = in.readSint16BE();
tmp.var1E = in.readSint16BE();
- seqList.push_back(tmp);
+ g_cine->_seqList.push_back(tmp);
}
return !(in.eos() || in.err());
}
bool loadZoneQuery(Common::SeekableReadStream &in) {
for (int i = 0; i < 16; i++) {
- zoneQuery[i] = in.readUint16BE();
+ g_cine->_zoneQuery[i] = in.readUint16BE();
}
return !(in.eos() || in.err());
}
@@ -330,19 +330,19 @@ void saveObjectTable(Common::OutSaveFile &out) {
out.writeUint16BE(0x20); // Entry size
for (int i = 0; i < NUM_MAX_OBJECT; i++) {
- out.writeUint16BE(objectTable[i].x);
- out.writeUint16BE(objectTable[i].y);
- out.writeUint16BE(objectTable[i].mask);
- out.writeUint16BE(objectTable[i].frame);
- out.writeUint16BE(objectTable[i].costume);
- out.write(objectTable[i].name, 20);
- out.writeUint16BE(objectTable[i].part);
+ out.writeUint16BE(g_cine->_objectTable[i].x);
+ out.writeUint16BE(g_cine->_objectTable[i].y);
+ out.writeUint16BE(g_cine->_objectTable[i].mask);
+ out.writeUint16BE(g_cine->_objectTable[i].frame);
+ out.writeUint16BE(g_cine->_objectTable[i].costume);
+ out.write(g_cine->_objectTable[i].name, 20);
+ out.writeUint16BE(g_cine->_objectTable[i].part);
}
}
void saveZoneData(Common::OutSaveFile &out) {
for (int i = 0; i < 16; i++) {
- out.writeUint16BE(zoneData[i]);
+ out.writeUint16BE(g_cine->_zoneData[i]);
}
}
@@ -356,8 +356,8 @@ void saveCommandVariables(Common::OutSaveFile &out) {
void saveCommandBuffer(Common::OutSaveFile &out) {
// Let's make sure there's space for the trailing zero
// (That's why we subtract one from the maximum command buffer size here).
- uint32 size = MIN<uint32>(commandBuffer.size(), kMaxCommandBufferSize - 1);
- out.write(commandBuffer.c_str(), size);
+ uint32 size = MIN<uint32>(g_cine->_commandBuffer.size(), kMaxCommandBufferSize - 1);
+ out.write(g_cine->_commandBuffer.c_str(), size);
// Write the rest as zeroes (Here we also write the string's trailing zero)
for (uint i = 0; i < kMaxCommandBufferSize - size; i++) {
out.writeByte(0);
@@ -369,7 +369,7 @@ void saveAnimDataTable(Common::OutSaveFile &out) {
out.writeUint16BE(0x1E); // Entry size
for (int i = 0; i < NUM_MAX_ANIMDATA; i++) {
- animDataTable[i].save(out);
+ g_cine->_animDataTable[i].save(out);
}
}
@@ -385,16 +385,16 @@ void saveScreenParams(Common::OutSaveFile &out) {
void saveGlobalScripts(Common::OutSaveFile &out) {
ScriptList::const_iterator it;
- out.writeUint16BE(globalScripts.size());
- for (it = globalScripts.begin(); it != globalScripts.end(); ++it) {
+ out.writeUint16BE(g_cine->_globalScripts.size());
+ for (it = g_cine->_globalScripts.begin(); it != g_cine->_globalScripts.end(); ++it) {
(*it)->save(out);
}
}
void saveObjectScripts(Common::OutSaveFile &out) {
ScriptList::const_iterator it;
- out.writeUint16BE(objectScripts.size());
- for (it = objectScripts.begin(); it != objectScripts.end(); ++it) {
+ out.writeUint16BE(g_cine->_objectScripts.size());
+ for (it = g_cine->_objectScripts.begin(); it != g_cine->_objectScripts.end(); ++it) {
(*it)->save(out);
}
}
@@ -402,9 +402,9 @@ void saveObjectScripts(Common::OutSaveFile &out) {
void saveOverlayList(Common::OutSaveFile &out) {
Common::List<overlay>::const_iterator it;
- out.writeUint16BE(overlayList.size());
+ out.writeUint16BE(g_cine->_overlayList.size());
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
out.writeUint32BE(0); // next
out.writeUint32BE(0); // previous?
out.writeUint16BE(it->objIdx);
@@ -418,9 +418,9 @@ void saveOverlayList(Common::OutSaveFile &out) {
void saveBgIncrustList(Common::OutSaveFile &out) {
Common::List<BGIncrust>::const_iterator it;
- out.writeUint16BE(bgIncrustList.size());
+ out.writeUint16BE(g_cine->_bgIncrustList.size());
- for (it = bgIncrustList.begin(); it != bgIncrustList.end(); ++it) {
+ for (it = g_cine->_bgIncrustList.begin(); it != g_cine->_bgIncrustList.end(); ++it) {
out.writeUint32BE(0); // next
out.writeUint32BE(0); // previous?
out.writeUint16BE(it->objIdx);
@@ -434,15 +434,15 @@ void saveBgIncrustList(Common::OutSaveFile &out) {
void saveZoneQuery(Common::OutSaveFile &out) {
for (int i = 0; i < 16; i++) {
- out.writeUint16BE(zoneQuery[i]);
+ out.writeUint16BE(g_cine->_zoneQuery[i]);
}
}
void saveSeqList(Common::OutSaveFile &out) {
Common::List<SeqListElement>::const_iterator it;
- out.writeUint16BE(seqList.size());
+ out.writeUint16BE(g_cine->_seqList.size());
- for (it = seqList.begin(); it != seqList.end(); ++it) {
+ for (it = g_cine->_seqList.begin(); it != g_cine->_seqList.end(); ++it) {
out.writeSint16BE(it->var4);
out.writeUint16BE(it->objIdx);
out.writeSint16BE(it->var8);
@@ -465,7 +465,7 @@ bool CineEngine::loadSaveDirectory() {
char tmp[80];
snprintf(tmp, 80, "%s.dir", _targetName.c_str());
- fHandle = g_saveFileMan->openForLoading(tmp);
+ fHandle = _saveFileMan->openForLoading(tmp);
if (!fHandle) {
return false;
@@ -566,14 +566,14 @@ bool CineEngine::loadTempSaveOS(Common::SeekableReadStream &in) {
}
loadObjectTable(in);
- renderer->restorePalette(in);
- globalVars.load(in, NUM_MAX_VAR);
+ renderer->restorePalette(in, hdr.version);
+ g_cine->_globalVars.load(in, NUM_MAX_VAR);
loadZoneData(in);
loadCommandVariables(in);
char tempCommandBuffer[kMaxCommandBufferSize];
in.read(tempCommandBuffer, kMaxCommandBufferSize);
- commandBuffer = tempCommandBuffer;
- renderer->setCommand(commandBuffer);
+ g_cine->_commandBuffer = tempCommandBuffer;
+ renderer->setCommand(g_cine->_commandBuffer);
loadZoneQuery(in);
// TODO: Use the loaded string (Current music name (String, 13 bytes)).
@@ -698,10 +698,10 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor
loadObjectTable(in);
// At 0x2043 (i.e. 0x005F + 2 * 2 + 255 * 32):
- renderer->restorePalette(in);
+ renderer->restorePalette(in, 0);
// At 0x2083 (i.e. 0x2043 + 16 * 2 * 2):
- globalVars.load(in, NUM_MAX_VAR);
+ g_cine->_globalVars.load(in, NUM_MAX_VAR);
// At 0x2281 (i.e. 0x2083 + 255 * 2):
loadZoneData(in);
@@ -712,8 +712,8 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor
// At 0x22A9 (i.e. 0x22A1 + 4 * 2):
char tempCommandBuffer[kMaxCommandBufferSize];
in.read(tempCommandBuffer, kMaxCommandBufferSize);
- commandBuffer = tempCommandBuffer;
- renderer->setCommand(commandBuffer);
+ g_cine->_commandBuffer = tempCommandBuffer;
+ renderer->setCommand(g_cine->_commandBuffer);
// At 0x22F9 (i.e. 0x22A9 + 0x50):
renderer->_cmdY = in.readUint16BE();
@@ -771,7 +771,7 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor
}
bool CineEngine::makeLoad(char *saveName) {
- Common::SharedPtr<Common::InSaveFile> saveFile(g_saveFileMan->openForLoading(saveName));
+ Common::SharedPtr<Common::InSaveFile> saveFile(_saveFileMan->openForLoading(saveName));
if (!saveFile) {
renderer->drawString(otherMessages[0], 0);
@@ -855,7 +855,7 @@ void CineEngine::makeSaveFW(Common::OutSaveFile &out) {
saveObjectTable(out);
renderer->savePalette(out);
- globalVars.save(out, NUM_MAX_VAR);
+ g_cine->_globalVars.save(out, NUM_MAX_VAR);
saveZoneData(out);
saveCommandVariables(out);
saveCommandBuffer(out);
@@ -912,7 +912,7 @@ void CineEngine::makeSaveOS(Common::OutSaveFile &out) {
saveObjectTable(out);
renderer->savePalette(out);
- globalVars.save(out, NUM_MAX_VAR);
+ g_cine->_globalVars.save(out, NUM_MAX_VAR);
saveZoneData(out);
saveCommandVariables(out);
saveCommandBuffer(out);
@@ -966,7 +966,7 @@ void CineEngine::makeSaveOS(Common::OutSaveFile &out) {
}
void CineEngine::makeSave(char *saveFileName) {
- Common::SharedPtr<Common::OutSaveFile> fHandle(g_saveFileMan->openForSaving(saveFileName));
+ Common::SharedPtr<Common::OutSaveFile> fHandle(_saveFileMan->openForSaving(saveFileName));
setMouseCursor(MOUSE_CURSOR_DISK);
@@ -976,7 +976,7 @@ void CineEngine::makeSave(char *saveFileName) {
// restoreScreen();
checkDataDisk(-1);
} else {
- if (g_cine->getGameType() == GType_FW) {
+ if (getGameType() == GType_FW) {
makeSaveFW(*fHandle);
} else {
makeSaveOS(*fHandle);
@@ -1045,7 +1045,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam
loadPart(name);
}
- animName = partBuffer[foundFileIdx].partName;
+ animName = g_cine->_partBuffer[foundFileIdx].partName;
loadRelatedPalette(animName); // Is this for Future Wars only?
const int16 prevAnim = currentAnim;
currentAnim = loadResource(animName, currentAnim);
diff --git a/engines/cine/saveload.h b/engines/cine/saveload.h
index 65f24f838d..a6e0e3f1ab 100644
--- a/engines/cine/saveload.h
+++ b/engines/cine/saveload.h
@@ -74,7 +74,7 @@ enum CineSaveGameFormat {
static const uint32 TEMP_OS_FORMAT_ID = MKID_BE('TEMP');
/** The current version number of Operation Stealth's savegame format. */
-static const uint32 CURRENT_OS_SAVE_VER = 0;
+static const uint32 CURRENT_OS_SAVE_VER = 1;
/** Chunk header used by the temporary Operation Stealth savegame format. */
struct ChunkHeader {
diff --git a/engines/cine/script.h b/engines/cine/script.h
index 1c3b496375..756bc930e8 100644
--- a/engines/cine/script.h
+++ b/engines/cine/script.h
@@ -67,6 +67,7 @@ public:
ScriptVars(const ScriptVars &src);
~ScriptVars();
+ void reinit(unsigned int len);
ScriptVars &operator=(const ScriptVars &src);
int16 &operator[](unsigned int idx);
int16 operator[](unsigned int idx) const;
@@ -368,9 +369,7 @@ typedef Common::Array<RawObjectScriptPtr> RawObjectScriptArray;
#define NUM_MAX_SCRIPT 50
-extern RawScriptArray scriptTable;
extern FWScriptInfo *scriptInfo;
-extern ScriptVars globalVars;
void setupOpcodes();
diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp
index e71fec8898..430a32ac69 100644
--- a/engines/cine/script_fw.cpp
+++ b/engines/cine/script_fw.cpp
@@ -38,14 +38,6 @@
namespace Cine {
-/**
- * Global variables.
- * 255 of these are saved, but there's one more that's used for bypassing the copy protection.
- * In CineEngine::mainLoop(int bootScriptIdx) there's this code: globalVars[VAR_BYPASS_PROTECTION] = 0;
- * And as VAR_BYPASS_PROTECTION is 255 that's why we're allocating one more than we otherwise would.
- */
-ScriptVars globalVars(NUM_MAX_VAR + 1);
-
uint16 compareVars(int16 a, int16 b);
@@ -216,7 +208,6 @@ void FWScript::setupTable() {
}
FWScriptInfo *scriptInfo; ///< Script factory
-RawScriptArray scriptTable; ///< Table of script bytecode
/**
* @todo replace with script subsystem
@@ -257,6 +248,14 @@ ScriptVars::ScriptVars(Common::SeekableReadStream &fHandle, unsigned int len)
load(fHandle);
}
+void ScriptVars::reinit(unsigned int len) {
+ delete _vars;
+
+ _size = len;
+ _vars = new int16[len];
+ reset();
+}
+
/**
* Copy constructor
*/
@@ -606,7 +605,7 @@ RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data,
FWScript::FWScript(const RawScript &script, int16 idx) : _script(script),
_pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
- _globalVars(globalVars), _info(new FWScriptInfo) { }
+ _globalVars(g_cine->_globalVars), _info(new FWScriptInfo) { }
/**
* Copy constructor
@@ -624,7 +623,7 @@ FWScript::FWScript(const FWScript &src) : _script(src._script), _pos(src._pos),
FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info) :
_script(script), _pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
- _globalVars(globalVars), _info(info) { }
+ _globalVars(g_cine->_globalVars), _info(info) { }
/**
* Constructor for object scripts in derived classes
@@ -634,7 +633,7 @@ FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info) :
FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) :
_script(script), _pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
- _globalVars(globalVars), _info(info) {
+ _globalVars(g_cine->_globalVars), _info(info) {
_localVars[0] = script.run();
}
@@ -964,11 +963,11 @@ int FWScript::o1_loadVar() {
break;
case 8:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = file[%d].packedSize", _line, varIdx, dataIdx);
- _localVars[varIdx] = partBuffer[dataIdx].packedSize;
+ _localVars[varIdx] = g_cine->_partBuffer[dataIdx].packedSize;
break;
case 9:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = file[%d].unpackedSize", _line, varIdx, dataIdx);
- _localVars[varIdx] = partBuffer[dataIdx].unpackedSize;
+ _localVars[varIdx] = g_cine->_partBuffer[dataIdx].unpackedSize;
break;
default:
error("executeScript: o1_loadVar: Unknown variable type %d", varType);
@@ -1196,7 +1195,7 @@ int FWScript::o1_addSpriteFilledToBgList() {
int FWScript::o1_op1B() {
debugC(5, kCineDebugScript, "Line: %d: freeBgIncrustList", _line);
- bgIncrustList.clear();
+ g_cine->_bgIncrustList.clear();
return 0;
}
@@ -1343,9 +1342,9 @@ int FWScript::o1_endGlobalScript() {
debugC(5, kCineDebugScript, "Line: %d: stopGlobalScript(%d)", _line, scriptIdx);
- ScriptList::iterator it = globalScripts.begin();
+ ScriptList::iterator it = g_cine->_globalScripts.begin();
- for (; it != globalScripts.end(); ++it) {
+ for (; it != g_cine->_globalScripts.end(); ++it) {
if ((*it)->_index == scriptIdx) {
(*it)->_index = -1;
}
@@ -1369,7 +1368,7 @@ int FWScript::o1_loadBg() {
debugC(5, kCineDebugScript, "Line: %d: loadBg(\"%s\")", _line, param);
loadBg(param);
- bgIncrustList.clear();
+ g_cine->_bgIncrustList.clear();
bgVar0 = 0;
return 0;
}
@@ -1627,7 +1626,7 @@ int FWScript::o1_freePartRange() {
int FWScript::o1_unloadAllMasks() {
debugC(5, kCineDebugScript, "Line: %d: unloadAllMasks()", _line);
- overlayList.clear();
+ g_cine->_overlayList.clear();
return 0;
}
@@ -1656,7 +1655,7 @@ int FWScript::o1_initializeZoneData() {
debugC(5, kCineDebugScript, "Line: %d: initializeZoneData()", _line);
for (int i = 0; i < NUM_MAX_ZONE; i++) {
- zoneData[i] = i;
+ g_cine->_zoneData[i] = i;
}
return 0;
}
@@ -1666,7 +1665,7 @@ int FWScript::o1_setZoneDataEntry() {
uint16 var = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: setZone[%d] = %d", _line, zoneIdx, var);
- zoneData[zoneIdx] = var;
+ g_cine->_zoneData[zoneIdx] = var;
return 0;
}
@@ -1674,7 +1673,7 @@ int FWScript::o1_getZoneDataEntry() {
byte zoneIdx = getNextByte();
byte var = getNextByte();
- _localVars[var] = zoneData[zoneIdx];
+ _localVars[var] = g_cine->_zoneData[zoneIdx];
return 0;
}
@@ -1796,7 +1795,7 @@ int FWScript::o1_playSample() {
int16 volume = getNextWord();
uint16 size = getNextWord();
- const byte *data = animDataTable[anim].data();
+ const byte *data = g_cine->_animDataTable[anim].data();
if (!data) {
return 0;
@@ -1804,7 +1803,7 @@ int FWScript::o1_playSample() {
if (g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) {
if (size == 0xFFFF) {
- size = animDataTable[anim]._width * animDataTable[anim]._height;
+ size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
}
if (channel < 10) { // || _currentOpcode == 0x78
int channel1, channel2;
@@ -1874,9 +1873,9 @@ int FWScript::o1_unloadMask5() {
//-----------------------------------------------------------------------
void addScriptToGlobalScripts(uint16 idx) {
- ScriptPtr tmp(scriptInfo->create(*scriptTable[idx], idx));
+ ScriptPtr tmp(scriptInfo->create(*g_cine->_scriptTable[idx], idx));
assert(tmp);
- globalScripts.push_back(tmp);
+ g_cine->_globalScripts.push_back(tmp);
}
int16 getZoneFromPosition(byte *page, int16 x, int16 y, int16 width) {
@@ -1916,8 +1915,8 @@ int16 getZoneFromPositionRaw(byte *page, int16 x, int16 y, int16 width) {
}
int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneIdx) {
- int16 lx = objectTable[objIdx].x + x;
- int16 ly = objectTable[objIdx].y + y;
+ int16 lx = g_cine->_objectTable[objIdx].x + x;
+ int16 ly = g_cine->_objectTable[objIdx].y + y;
int16 idx;
int16 result = 0;
@@ -1935,12 +1934,12 @@ int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneI
// The zoneQuery table is updated here only in Operation Stealth
if (g_cine->getGameType() == Cine::GType_OS) {
- if (zoneData[idx] < NUM_MAX_ZONE) {
- zoneQuery[zoneData[idx]]++;
+ if (g_cine->_zoneData[idx] < NUM_MAX_ZONE) {
+ g_cine->_zoneQuery[g_cine->_zoneData[idx]]++;
}
}
- if (zoneData[idx] == zoneIdx) {
+ if (g_cine->_zoneData[idx] == zoneIdx) {
result = 1;
// Future Wars breaks out early on the first match, but
// Operation Stealth doesn't because it needs to update
@@ -1969,10 +1968,10 @@ uint16 compareVars(int16 a, int16 b) {
}
void executeObjectScripts() {
- ScriptList::iterator it = objectScripts.begin();
- for (; it != objectScripts.end();) {
+ ScriptList::iterator it = g_cine->_objectScripts.begin();
+ for (; it != g_cine->_objectScripts.end();) {
if ((*it)->_index < 0 || (*it)->execute() < 0) {
- it = objectScripts.erase(it);
+ it = g_cine->_objectScripts.erase(it);
} else {
++it;
}
@@ -1980,10 +1979,10 @@ void executeObjectScripts() {
}
void executeGlobalScripts() {
- ScriptList::iterator it = globalScripts.begin();
- for (; it != globalScripts.end();) {
+ ScriptList::iterator it = g_cine->_globalScripts.begin();
+ for (; it != g_cine->_globalScripts.end();) {
if ((*it)->_index < 0 || (*it)->execute() < 0) {
- it = globalScripts.erase(it);
+ it = g_cine->_globalScripts.erase(it);
} else {
++it;
}
diff --git a/engines/cine/script_os.cpp b/engines/cine/script_os.cpp
index d03b118443..ab1ad7ff9c 100644
--- a/engines/cine/script_os.cpp
+++ b/engines/cine/script_os.cpp
@@ -420,16 +420,16 @@ int FWScript::o2_playSampleAlt() {
uint16 size = getNextWord();
if (size == 0xFFFF) {
- size = animDataTable[num]._width * animDataTable[num]._height;
+ size = g_cine->_animDataTable[num]._width * g_cine->_animDataTable[num]._height;
}
- if (animDataTable[num].data()) {
+ if (g_cine->_animDataTable[num].data()) {
if (g_cine->getPlatform() == Common::kPlatformPC) {
// if speaker output is available, play sound on it
// if it's another device, don't play anything
// TODO: implement this, it's used in the introduction for example
// on each letter displayed
} else {
- g_sound->playSound(channel, frequency, animDataTable[num].data(), size, 0, 0, 63, 0);
+ g_sound->playSound(channel, frequency, g_cine->_animDataTable[num].data(), size, 0, 0, 63, 0);
}
}
return 0;
@@ -611,9 +611,9 @@ int FWScript::o2_stopObjectScript() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: stopObjectScript(%d)", _line, param);
- ScriptList::iterator it = objectScripts.begin();
+ ScriptList::iterator it = g_cine->_objectScripts.begin();
- for (; it != objectScripts.end(); ++it) {
+ for (; it != g_cine->_objectScripts.end(); ++it) {
if ((*it)->_index == param) {
(*it)->_index = -1;
}
@@ -699,7 +699,7 @@ int FWScript::o2_loadBg() {
int FWScript::o2_wasZoneChecked() {
byte param = getNextByte();
- _compare = (param < NUM_MAX_ZONE && zoneQuery[param]) ? 1 : 0;
+ _compare = (param < NUM_MAX_ZONE && g_cine->_zoneQuery[param]) ? 1 : 0;
debugC(5, kCineDebugScript, "Line: %d: o2_wasZoneChecked(%d)", _line, param);
return 0;
}
diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp
index 9a10c2b5d7..cf7135a6b5 100644
--- a/engines/cine/various.cpp
+++ b/engines/cine/various.cpp
@@ -81,7 +81,6 @@ uint16 _messageLen;
int16 playerCommand;
-Common::String commandBuffer;
char currentPrcName[20];
char currentRelName[20];
char currentObjectName[20];
@@ -137,9 +136,6 @@ static const int16 canUseOnItemTable[] = { 1, 0, 0, 1, 1, 0, 0 };
CommandeType objectListCommand[20];
int16 objListTab[20];
-Common::Array<uint16> zoneData;
-Common::Array<uint16> zoneQuery; ///< Only exists in Operation Stealth
-
/**
* Move the player character using the keyboard
* @param x Negative values move left, positive right, zero not at all
@@ -174,9 +170,9 @@ void stopMusicAfterFadeOut() {
}
void runObjectScript(int16 entryIdx) {
- ScriptPtr tmp(scriptInfo->create(*relTable[entryIdx], entryIdx));
+ ScriptPtr tmp(scriptInfo->create(*g_cine->_relTable[entryIdx], entryIdx));
assert(tmp);
- objectScripts.push_back(tmp);
+ g_cine->_objectScripts.push_back(tmp);
}
/**
@@ -190,19 +186,19 @@ void addPlayerCommandMessage(int16 cmd) {
tmp.objIdx = cmd;
tmp.type = 3;
- overlayList.push_back(tmp);
+ g_cine->_overlayList.push_back(tmp);
}
int16 getRelEntryForObject(uint16 param1, uint16 param2, SelectedObjStruct *pSelectedObject) {
int16 i;
int16 found = -1;
- for (i = 0; i < (int16)relTable.size(); i++) {
- if (relTable[i]->_param1 == param1 && relTable[i]->_param2 == pSelectedObject->idx) {
+ for (i = 0; i < (int16)g_cine->_relTable.size(); i++) {
+ if (g_cine->_relTable[i]->_param1 == param1 && g_cine->_relTable[i]->_param2 == pSelectedObject->idx) {
if (param2 == 1) {
found = i;
} else if (param2 == 2) {
- if (relTable[i]->_param3 == pSelectedObject->param) {
+ if (g_cine->_relTable[i]->_param3 == pSelectedObject->param) {
found = i;
}
}
@@ -228,19 +224,19 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) {
int width;
// reverse_iterator would be nice
- for (it = overlayList.reverse_begin(); it != overlayList.end(); --it) {
- if (it->type >= 2 || !objectTable[it->objIdx].name[0]) {
+ for (it = g_cine->_overlayList.reverse_begin(); it != g_cine->_overlayList.end(); --it) {
+ if (it->type >= 2 || !g_cine->_objectTable[it->objIdx].name[0]) {
continue;
}
- objX = objectTable[it->objIdx].x;
- objY = objectTable[it->objIdx].y;
+ objX = g_cine->_objectTable[it->objIdx].x;
+ objY = g_cine->_objectTable[it->objIdx].y;
- frame = ABS((int16)(objectTable[it->objIdx].frame));
- part = objectTable[it->objIdx].part;
+ frame = ABS((int16)(g_cine->_objectTable[it->objIdx].frame));
+ part = g_cine->_objectTable[it->objIdx].part;
// Additional case for negative frame values in Operation Stealth
- if (g_cine->getGameType() == Cine::GType_OS && objectTable[it->objIdx].frame < 0) {
+ if (g_cine->getGameType() == Cine::GType_OS && g_cine->_objectTable[it->objIdx].frame < 0) {
if ((it->type == 1) && (x >= objX) && (objX + frame >= x) && (y >= objY) && (objY + part >= y)) {
return it->objIdx;
} else {
@@ -249,18 +245,18 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) {
}
if (it->type == 0) {
- threshold = animDataTable[frame]._var1;
+ threshold = g_cine->_animDataTable[frame]._var1;
} else {
- threshold = animDataTable[frame]._width / 2;
+ threshold = g_cine->_animDataTable[frame]._width / 2;
}
- height = animDataTable[frame]._height;
- width = animDataTable[frame]._realWidth;
+ height = g_cine->_animDataTable[frame]._height;
+ width = g_cine->_animDataTable[frame]._realWidth;
xdif = x - objX;
ydif = y - objY;
- if ((xdif < 0) || ((threshold << 4) <= xdif) || (ydif <= 0) || (ydif >= height) || !animDataTable[frame].data()) {
+ if ((xdif < 0) || ((threshold << 4) <= xdif) || (ydif <= 0) || (ydif >= height) || !g_cine->_animDataTable[frame].data()) {
continue;
}
@@ -272,17 +268,17 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) {
continue;
}
- if (it->type == 0 && animDataTable[frame].getColor(xdif, ydif) != (part & 0x0F)) {
+ if (it->type == 0 && g_cine->_animDataTable[frame].getColor(xdif, ydif) != (part & 0x0F)) {
return it->objIdx;
- } else if (it->type == 1 && gfxGetBit(xdif, ydif, animDataTable[frame].data(), animDataTable[frame]._width * 4)) {
+ } else if (it->type == 1 && gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].data(), g_cine->_animDataTable[frame]._width * 4)) {
return it->objIdx;
}
} else if (it->type == 0) { // use generated mask
- if (gfxGetBit(xdif, ydif, animDataTable[frame].mask(), animDataTable[frame]._width)) {
+ if (gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].mask(), g_cine->_animDataTable[frame]._width)) {
return it->objIdx;
}
} else if (it->type == 1) { // is mask
- if (gfxGetBit(xdif, ydif, animDataTable[frame].data(), animDataTable[frame]._width * 4)) {
+ if (gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].data(), g_cine->_animDataTable[frame]._width * 4)) {
return it->objIdx;
}
}
@@ -294,18 +290,18 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) {
void CineEngine::resetEngine() {
g_sound->stopMusic();
freeAnimDataTable();
- overlayList.clear();
- bgIncrustList.clear();
+ g_cine->_overlayList.clear();
+ g_cine->_bgIncrustList.clear();
closePart();
- objectScripts.clear();
- globalScripts.clear();
- relTable.clear();
- scriptTable.clear();
- messageTable.clear();
+ g_cine->_objectScripts.clear();
+ g_cine->_globalScripts.clear();
+ g_cine->_relTable.clear();
+ g_cine->_scriptTable.clear();
+ g_cine->_messageTable.clear();
resetObjectTable();
- globalVars.reset();
+ g_cine->_globalVars.reset();
var2 = var3 = var4 = var5 = 0;
@@ -320,10 +316,10 @@ void CineEngine::resetEngine() {
playerCommand = -1;
isDrawCommandEnabled = 0;
- commandBuffer = "";
+ g_cine->_commandBuffer = "";
- globalVars[VAR_MOUSE_X_POS] = 0;
- globalVars[VAR_MOUSE_Y_POS] = 0;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = 0;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = 0;
fadeRequired = false;
@@ -332,7 +328,7 @@ void CineEngine::resetEngine() {
checkForPendingDataLoadSwitch = 0;
if (g_cine->getGameType() == Cine::GType_OS) {
- seqList.clear();
+ g_cine->_seqList.clear();
currentAdditionalBgIdx = 0;
currentAdditionalBgIdx2 = 0;
// TODO: Add resetting of the following variables
@@ -450,7 +446,7 @@ void CineEngine::makeSystemMenu() {
snprintf(tmp, 80, "%s.dir", _targetName.c_str());
- Common::OutSaveFile *fHandle = g_saveFileMan->openForSaving(tmp);
+ Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp);
if (!fHandle) {
warning("Unable to open file %s for saving", tmp);
break;
@@ -539,8 +535,8 @@ int16 buildObjectListCommand(int16 param) {
}
for (i = 0; i < 255; i++) {
- if (objectTable[i].name[0] && objectTable[i].costume == param) {
- strcpy(objectListCommand[j], objectTable[i].name);
+ if (g_cine->_objectTable[i].name[0] && g_cine->_objectTable[i].costume == param) {
+ strcpy(objectListCommand[j], g_cine->_objectTable[i].name);
objListTab[j] = i;
j++;
}
@@ -581,9 +577,9 @@ void makeCommandLine() {
commandVar2 = -10;
if (playerCommand != -1) {
- commandBuffer = defaultActionCommand[playerCommand];
+ g_cine->_commandBuffer = defaultActionCommand[playerCommand];
} else {
- commandBuffer = "";
+ g_cine->_commandBuffer = "";
}
if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection ?
@@ -602,7 +598,7 @@ void makeCommandLine() {
canUseOnObject = 0;
} else { // Future Wars
playerCommand = -1;
- commandBuffer = "";
+ g_cine->_commandBuffer = "";
}
} else {
if (g_cine->getGameType() == Cine::GType_OS) {
@@ -616,13 +612,13 @@ void makeCommandLine() {
commandVar3[0] = si;
commandVar1 = 1;
- commandBuffer += " ";
- commandBuffer += objectTable[commandVar3[0]].name;
- commandBuffer += " ";
+ g_cine->_commandBuffer += " ";
+ g_cine->_commandBuffer += g_cine->_objectTable[commandVar3[0]].name;
+ g_cine->_commandBuffer += " ";
if (g_cine->getGameType() == Cine::GType_OS) {
- commandBuffer += commandPrepositionTable[playerCommand];
+ g_cine->_commandBuffer += commandPrepositionTable[playerCommand];
} else { // Future Wars
- commandBuffer += defaultCommandPreposition;
+ g_cine->_commandBuffer += defaultCommandPreposition;
}
}
}
@@ -634,7 +630,7 @@ void makeCommandLine() {
processInventory(x, y + 8);
playerCommand = -1;
commandVar1 = 0;
- commandBuffer = "";
+ g_cine->_commandBuffer = "";
CursorMan.showMouse(true);
}
}
@@ -654,8 +650,8 @@ void makeCommandLine() {
commandVar3[commandVar1] = si;
commandVar1++;
- commandBuffer += " ";
- commandBuffer += objectTable[si].name;
+ g_cine->_commandBuffer += " ";
+ g_cine->_commandBuffer += g_cine->_objectTable[si].name;
}
}
@@ -673,13 +669,13 @@ void makeCommandLine() {
playerCommand = -1;
commandVar1 = 0;
- commandBuffer = "";
+ g_cine->_commandBuffer = "";
}
}
if (g_cine->getGameType() == Cine::GType_OS || !disableSystemMenu) {
isDrawCommandEnabled = 1;
- renderer->setCommand(commandBuffer);
+ renderer->setCommand(g_cine->_commandBuffer);
}
}
@@ -858,7 +854,7 @@ uint16 executePlayerInput() {
if (allowPlayerInput) { // Player input is allowed
if (isDrawCommandEnabled) {
- renderer->setCommand(commandBuffer);
+ renderer->setCommand(g_cine->_commandBuffer);
}
isDrawCommandEnabled = 0;
limitMouseCheckCount = true;
@@ -906,8 +902,8 @@ uint16 executePlayerInput() {
commandVar3[commandVar1] = si;
commandVar1++;
- commandBuffer += " ";
- commandBuffer += objectTable[si].name;
+ g_cine->_commandBuffer += " ";
+ g_cine->_commandBuffer += g_cine->_objectTable[si].name;
isDrawCommandEnabled = 1;
@@ -929,27 +925,27 @@ uint16 executePlayerInput() {
playerCommand = -1;
commandVar1 = 0;
- commandBuffer = "";
+ g_cine->_commandBuffer = "";
} else if (g_cine->getGameType() == Cine::GType_OS) {
isDrawCommandEnabled = 1;
- commandBuffer += commandPrepositionTable[playerCommand];
+ g_cine->_commandBuffer += commandPrepositionTable[playerCommand];
}
- renderer->setCommand(commandBuffer);
+ renderer->setCommand(g_cine->_commandBuffer);
} else {
- globalVars[VAR_MOUSE_X_POS] = mouseX;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = mouseX;
if (!mouseX) {
- globalVars[VAR_MOUSE_X_POS]++;
+ g_cine->_globalVars[VAR_MOUSE_X_POS]++;
}
- globalVars[VAR_MOUSE_Y_POS] = mouseY;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = mouseY;
if (g_cine->getGameType() == Cine::GType_OS) {
if (!mouseY) {
- globalVars[VAR_MOUSE_Y_POS]++;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS]++;
}
- globalVars[VAR_MOUSE_X_POS_2ND] = globalVars[VAR_MOUSE_X_POS];
- globalVars[VAR_MOUSE_Y_POS_2ND] = globalVars[VAR_MOUSE_Y_POS];
+ g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = g_cine->_globalVars[VAR_MOUSE_X_POS];
+ g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = g_cine->_globalVars[VAR_MOUSE_Y_POS];
}
}
}
@@ -961,7 +957,7 @@ uint16 executePlayerInput() {
if (g_cine->getGameType() == Cine::GType_OS || commandVar2 != objIdx) {
if (objIdx != -1) {
- renderer->setCommand(commandBuffer + " " + objectTable[objIdx].name);
+ renderer->setCommand(g_cine->_commandBuffer + " " + g_cine->_objectTable[objIdx].name);
} else {
isDrawCommandEnabled = 1;
}
@@ -976,19 +972,19 @@ uint16 executePlayerInput() {
int16 objIdx;
int16 relEntry;
- globalVars[VAR_MOUSE_X_POS] = mouseX;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = mouseX;
if (!mouseX) {
- globalVars[VAR_MOUSE_X_POS]++;
+ g_cine->_globalVars[VAR_MOUSE_X_POS]++;
}
- globalVars[VAR_MOUSE_Y_POS] = mouseY;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = mouseY;
if (g_cine->getGameType() == Cine::GType_OS) {
if (!mouseY) {
- globalVars[VAR_MOUSE_Y_POS]++;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS]++;
}
- globalVars[VAR_MOUSE_X_POS_2ND] = globalVars[VAR_MOUSE_X_POS];
- globalVars[VAR_MOUSE_Y_POS_2ND] = globalVars[VAR_MOUSE_Y_POS];
+ g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = g_cine->_globalVars[VAR_MOUSE_X_POS];
+ g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = g_cine->_globalVars[VAR_MOUSE_Y_POS];
}
objIdx = getObjectUnderCursor(mouseX, mouseY);
@@ -1020,97 +1016,97 @@ uint16 executePlayerInput() {
// Handle possible horizontal movement by keyboard
if (xMoveKeyb != kKeybMoveCenterX && allowPlayerInput) {
if (xMoveKeyb == kKeybMoveRight) { // moving right
- const int16 playerFrame = objectTable[1].frame;
- const int16 playerX = objectTable[1].x;
+ const int16 playerFrame = g_cine->_objectTable[1].frame;
+ const int16 playerX = g_cine->_objectTable[1].x;
// TODO: Check if multiplying _width by two here is correct or not
- const int16 newX = animDataTable[playerFrame]._width * 2 + playerX + 8;
- globalVars[VAR_MOUSE_X_POS] = globalVars[VAR_MOUSE_X_POS_2ND] = newX;
+ const int16 newX = g_cine->_animDataTable[playerFrame]._width * 2 + playerX + 8;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = newX;
} else { // moving left
- const int16 playerX = objectTable[1].x;
+ const int16 playerX = g_cine->_objectTable[1].x;
const int16 newX = playerX - 8;
- globalVars[VAR_MOUSE_X_POS] = globalVars[VAR_MOUSE_X_POS_2ND] = newX;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = newX;
}
// Restrain horizontal position to range 0-319
- if (globalVars[VAR_MOUSE_X_POS] < 0) {
- globalVars[VAR_MOUSE_X_POS] = globalVars[VAR_MOUSE_X_POS_2ND] = 0;
- } else if (globalVars[VAR_MOUSE_X_POS] > 319) {
- globalVars[VAR_MOUSE_X_POS] = globalVars[VAR_MOUSE_X_POS_2ND] = 319;
+ if (g_cine->_globalVars[VAR_MOUSE_X_POS] < 0) {
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = 0;
+ } else if (g_cine->_globalVars[VAR_MOUSE_X_POS] > 319) {
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = 319;
}
}
// Handle possible vertical movement by keyboard
if (yMoveKeyb != kKeybMoveCenterY && allowPlayerInput) {
if (yMoveKeyb == kKeybMoveDown) { // moving down
- const int16 playerFrame = objectTable[1].frame;
- const int16 playerY = objectTable[1].y;
+ const int16 playerFrame = g_cine->_objectTable[1].frame;
+ const int16 playerY = g_cine->_objectTable[1].y;
// TODO: Check if multiplying _height by two here is correct or not
- const int16 newY = animDataTable[playerFrame]._height * 2 + playerY - 1;
- globalVars[VAR_MOUSE_Y_POS] = globalVars[VAR_MOUSE_Y_POS_2ND] = newY;
+ const int16 newY = g_cine->_animDataTable[playerFrame]._height * 2 + playerY - 1;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = newY;
} else { // moving up
- const int16 playerY = objectTable[1].y;
+ const int16 playerY = g_cine->_objectTable[1].y;
const int16 newY = playerY - 8;
- globalVars[VAR_MOUSE_Y_POS] = globalVars[VAR_MOUSE_Y_POS_2ND] = newY;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = newY;
}
// Restrain vertical position to range 0-199
- if (globalVars[VAR_MOUSE_Y_POS] < 0) {
- globalVars[VAR_MOUSE_Y_POS] = globalVars[VAR_MOUSE_Y_POS_2ND] = 0;
- } else if (globalVars[VAR_MOUSE_Y_POS] > 199) {
- globalVars[VAR_MOUSE_Y_POS] = globalVars[VAR_MOUSE_Y_POS_2ND] = 199;
+ if (g_cine->_globalVars[VAR_MOUSE_Y_POS] < 0) {
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = 0;
+ } else if (g_cine->_globalVars[VAR_MOUSE_Y_POS] > 199) {
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = 199;
}
}
} else if (egoMovedWithKeyboard && allowPlayerInput) { // FW: Move using keyboard
egoMovedWithKeyboard = false;
- switch (globalVars[VAR_MOUSE_X_MODE]) {
+ switch (g_cine->_globalVars[VAR_MOUSE_X_MODE]) {
case 1:
- mouseX = objectTable[1].x + 12;
+ mouseX = g_cine->_objectTable[1].x + 12;
break;
case 2:
- mouseX = objectTable[1].x + 7;
+ mouseX = g_cine->_objectTable[1].x + 7;
break;
default:
- mouseX = globalVars[VAR_MOUSE_X_POS];
+ mouseX = g_cine->_globalVars[VAR_MOUSE_X_POS];
break;
}
- switch (globalVars[VAR_MOUSE_Y_MODE]) {
+ switch (g_cine->_globalVars[VAR_MOUSE_Y_MODE]) {
case 1:
- mouseY = objectTable[1].y + 34;
+ mouseY = g_cine->_objectTable[1].y + 34;
break;
case 2:
- mouseY = objectTable[1].y + 28;
+ mouseY = g_cine->_objectTable[1].y + 28;
break;
default:
- mouseY = globalVars[VAR_MOUSE_Y_POS];
+ mouseY = g_cine->_globalVars[VAR_MOUSE_Y_POS];
break;
}
if (var_5E == bgVar0) {
var_5E = 0;
- globalVars[VAR_MOUSE_X_POS] = mouseX;
- globalVars[VAR_MOUSE_Y_POS] = mouseY;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = mouseX;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = mouseY;
} else {
if (xMoveKeyb) {
if (xMoveKeyb == kKeybMoveLeft) {
- globalVars[VAR_MOUSE_X_POS] = 1;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = 1;
} else {
- globalVars[VAR_MOUSE_X_POS] = 320;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = 320;
}
} else {
- globalVars[VAR_MOUSE_X_POS] = mouseX;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = mouseX;
}
if (yMoveKeyb) {
if (yMoveKeyb == kKeybMoveUp) {
- globalVars[VAR_MOUSE_Y_POS] = 1;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = 1;
} else {
- globalVars[VAR_MOUSE_Y_POS] = 200;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = 200;
}
} else {
- globalVars[VAR_MOUSE_Y_POS] = mouseY;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = mouseY;
}
}
@@ -1167,27 +1163,27 @@ void drawSprite(Common::List<overlay>::iterator it, const byte *spritePtr, const
msk = (byte *)malloc(width * height);
if (g_cine->getGameType() == Cine::GType_OS) {
- generateMask(spritePtr, msk, width * height, objectTable[it->objIdx].part);
+ generateMask(spritePtr, msk, width * height, g_cine->_objectTable[it->objIdx].part);
} else {
memcpy(msk, maskPtr, width * height);
}
- for (++it; it != overlayList.end(); ++it) {
+ for (++it; it != g_cine->_overlayList.end(); ++it) {
if (it->type != 5) {
continue;
}
- maskX = objectTable[it->objIdx].x;
- maskY = objectTable[it->objIdx].y;
+ maskX = g_cine->_objectTable[it->objIdx].x;
+ maskY = g_cine->_objectTable[it->objIdx].y;
- maskSpriteIdx = ABS((int16)(objectTable[it->objIdx].frame));
+ maskSpriteIdx = ABS((int16)(g_cine->_objectTable[it->objIdx].frame));
- maskWidth = animDataTable[maskSpriteIdx]._realWidth;
- maskHeight = animDataTable[maskSpriteIdx]._height;
- gfxUpdateSpriteMask(msk, x, y, width, height, animDataTable[maskSpriteIdx].data(), maskX, maskY, maskWidth, maskHeight);
+ maskWidth = g_cine->_animDataTable[maskSpriteIdx]._realWidth;
+ maskHeight = g_cine->_animDataTable[maskSpriteIdx]._height;
+ gfxUpdateSpriteMask(msk, x, y, width, height, g_cine->_animDataTable[maskSpriteIdx].data(), maskX, maskY, maskWidth, maskHeight);
#ifdef DEBUG_SPRITE_MASK
- gfxFillSprite(animDataTable[maskSpriteIdx].data(), maskWidth, maskHeight, page, maskX, maskY, 1);
+ gfxFillSprite(g_cine->_animDataTable[maskSpriteIdx].data(), maskWidth, maskHeight, page, maskX, maskY, 1);
#endif
}
@@ -1199,7 +1195,7 @@ void removeMessages() {
Common::List<overlay>::iterator it;
bool remove;
- for (it = overlayList.begin(); it != overlayList.end(); ) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ) {
if (g_cine->getGameType() == Cine::GType_OS) {
// NOTE: These are really removeOverlay calls that have been deferred.
// In Operation Stealth's disassembly elements are removed from the
@@ -1213,7 +1209,7 @@ void removeMessages() {
}
if (remove) {
- it = overlayList.erase(it);
+ it = g_cine->_overlayList.erase(it);
} else {
++it;
}
@@ -1255,7 +1251,7 @@ void checkForPendingDataLoad() {
}
if (newObjectName[0] != 0) {
- overlayList.clear();
+ g_cine->_overlayList.clear();
loadObject(newObjectName);
@@ -1294,15 +1290,13 @@ void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 par
tmp.width = param4;
tmp.color = param5;
- overlayList.push_back(tmp);
+ g_cine->_overlayList.push_back(tmp);
}
-Common::List<SeqListElement> seqList;
-
void removeSeq(uint16 param1, uint16 param2, uint16 param3) {
Common::List<SeqListElement>::iterator it;
- for (it = seqList.begin(); it != seqList.end(); ++it) {
+ for (it = g_cine->_seqList.begin(); it != g_cine->_seqList.end(); ++it) {
if (it->objIdx == param1 && it->var4 == param2 && it->varE == param3) {
it->var4 = -1;
break;
@@ -1313,7 +1307,7 @@ void removeSeq(uint16 param1, uint16 param2, uint16 param3) {
bool isSeqRunning(uint16 param1, uint16 param2, uint16 param3) {
Common::List<SeqListElement>::iterator it;
- for (it = seqList.begin(); it != seqList.end(); ++it) {
+ for (it = g_cine->_seqList.begin(); it != g_cine->_seqList.end(); ++it) {
if (it->objIdx == param1 && it->var4 == param2 && it->varE == param3) {
// Just to be on the safe side there's a restriction of the
// addition's result to 16-bit arithmetic here like in the
@@ -1329,7 +1323,7 @@ void addSeqListElement(uint16 objIdx, int16 param1, int16 param2, int16 frame, i
Common::List<SeqListElement>::iterator it;
SeqListElement tmp;
- for (it = seqList.begin(); it != seqList.end() && it->varE < param7; ++it) ;
+ for (it = g_cine->_seqList.begin(); it != g_cine->_seqList.end() && it->varE < param7; ++it) ;
tmp.objIdx = objIdx;
tmp.var4 = param1;
@@ -1346,12 +1340,12 @@ void addSeqListElement(uint16 objIdx, int16 param1, int16 param2, int16 frame, i
tmp.var1C = 0;
tmp.var1E = 0;
- seqList.insert(it, tmp);
+ g_cine->_seqList.insert(it, tmp);
}
void modifySeqListElement(uint16 objIdx, int16 var4Test, int16 param1, int16 param2, int16 param3, int16 param4) {
// Find a suitable list element and modify it
- for (Common::List<SeqListElement>::iterator it = seqList.begin(); it != seqList.end(); ++it) {
+ for (Common::List<SeqListElement>::iterator it = g_cine->_seqList.begin(); it != g_cine->_seqList.end(); ++it) {
if (it->objIdx == objIdx && it->var4 == var4Test) {
it->varC = param1;
it->var18 = param2;
@@ -1425,7 +1419,7 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele
// In the original an error string is set and 0 is returned if the following doesn't hold
assert(*ptrData);
- di = (objectTable[objIdx].costume + 1) % (*ptrData);
+ di = (g_cine->_objectTable[objIdx].costume + 1) % (*ptrData);
++ptrData; // Jump over the just read byte
// Here ptr2 seems to be indexing a table of structs (8 bytes per struct):
// struct {
@@ -1446,18 +1440,18 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele
return 0;
}
- objectTable[objIdx].x += ptr2[4];
- objectTable[objIdx].y += ptr2[5];
- objectTable[objIdx].mask += ptr2[6];
+ g_cine->_objectTable[objIdx].x += ptr2[4];
+ g_cine->_objectTable[objIdx].y += ptr2[5];
+ g_cine->_objectTable[objIdx].mask += ptr2[6];
if (ptr2[6]) {
resetGfxEntityEntry(objIdx);
}
- objectTable[objIdx].frame = ptr2[7] + element.var8;
+ g_cine->_objectTable[objIdx].frame = ptr2[7] + element.var8;
if (param3 || !element.var14) {
- objectTable[objIdx].costume = di;
+ g_cine->_objectTable[objIdx].costume = di;
} else {
assert(param4);
*param4 = di;
@@ -1476,7 +1470,7 @@ void resetGfxEntityEntry(uint16 objIdx) {
bool foundCutPoint = false;
// Go through the overlay list and partition the whole list into two categories (Type A and type B objects)
- for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
if (it->objIdx == objIdx && it->type != 2 && it->type != 3) { // Type A object
aReverseObjs.push_front(*it);
} else { // Type B object
@@ -1485,10 +1479,10 @@ void resetGfxEntityEntry(uint16 objIdx) {
if (it->type == 2 || it->type == 3) {
objectMask = 10000;
} else {
- objectMask = objectTable[it->objIdx].mask;
+ objectMask = g_cine->_objectTable[it->objIdx].mask;
}
- if (objectTable[objIdx].mask > objectMask) { // Check for B objects' cut point
+ if (g_cine->_objectTable[objIdx].mask > objectMask) { // Check for B objects' cut point
bObjsCutPoint = bObjs.reverse_begin();
foundCutPoint = true;
}
@@ -1496,26 +1490,26 @@ void resetGfxEntityEntry(uint16 objIdx) {
}
// Recreate the overlay list in a different order.
- overlayList.clear();
+ g_cine->_overlayList.clear();
if (foundCutPoint) {
// If a cut point was found the order is:
// B objects before the cut point, the cut point, A objects in reverse order, B objects after cut point.
++bObjsCutPoint; // Include the cut point in the first list insertion
- overlayList.insert(overlayList.end(), bObjs.begin(), bObjsCutPoint);
- overlayList.insert(overlayList.end(), aReverseObjs.begin(), aReverseObjs.end());
- overlayList.insert(overlayList.end(), bObjsCutPoint, bObjs.end());
+ g_cine->_overlayList.insert(g_cine->_overlayList.end(), bObjs.begin(), bObjsCutPoint);
+ g_cine->_overlayList.insert(g_cine->_overlayList.end(), aReverseObjs.begin(), aReverseObjs.end());
+ g_cine->_overlayList.insert(g_cine->_overlayList.end(), bObjsCutPoint, bObjs.end());
} else {
// If no cut point was found the order is:
// A objects in reverse order, B objects.
- overlayList.insert(overlayList.end(), aReverseObjs.begin(), aReverseObjs.end());
- overlayList.insert(overlayList.end(), bObjs.begin(), bObjs.end());
+ g_cine->_overlayList.insert(g_cine->_overlayList.end(), aReverseObjs.begin(), aReverseObjs.end());
+ g_cine->_overlayList.insert(g_cine->_overlayList.end(), bObjs.begin(), bObjs.end());
}
}
void processSeqListElement(SeqListElement &element) {
- int16 x = objectTable[element.objIdx].x;
- int16 y = objectTable[element.objIdx].y;
- const int8 *ptr1 = (const int8 *) animDataTable[element.frame].data();
+ int16 x = g_cine->_objectTable[element.objIdx].x;
+ int16 y = g_cine->_objectTable[element.objIdx].y;
+ const int8 *ptr1 = (const int8 *) g_cine->_animDataTable[element.frame].data();
int16 var_10;
int16 var_4;
int16 var_2;
@@ -1548,8 +1542,8 @@ void processSeqListElement(SeqListElement &element) {
int16 x2 = element.var18;
int16 y2 = element.var1A;
if (element.varC) {
- x2 += objectTable[element.varC].x;
- y2 += objectTable[element.varC].y;
+ x2 += g_cine->_objectTable[element.varC].x;
+ y2 += g_cine->_objectTable[element.varC].y;
}
computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, x2, y2);
} else {
@@ -1558,7 +1552,7 @@ void processSeqListElement(SeqListElement &element) {
if (xMoveKeyb != kKeybMoveRight) {
adder = -adder;
}
- globalVars[VAR_MOUSE_X_POS] = globalVars[VAR_MOUSE_X_POS_2ND] = ptr1[4] + x + adder;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = ptr1[4] + x + adder;
}
if (yMoveKeyb && allowPlayerInput) {
@@ -1566,11 +1560,11 @@ void processSeqListElement(SeqListElement &element) {
if (yMoveKeyb != kKeybMoveDown) {
adder = -adder;
}
- globalVars[VAR_MOUSE_Y_POS] = globalVars[VAR_MOUSE_Y_POS_2ND] = ptr1[5] + y + adder;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = ptr1[5] + y + adder;
}
- if (globalVars[VAR_MOUSE_X_POS] || globalVars[VAR_MOUSE_Y_POS]) {
- computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, globalVars[VAR_MOUSE_X_POS], globalVars[VAR_MOUSE_Y_POS]);
+ if (g_cine->_globalVars[VAR_MOUSE_X_POS] || g_cine->_globalVars[VAR_MOUSE_Y_POS]) {
+ computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, g_cine->_globalVars[VAR_MOUSE_X_POS], g_cine->_globalVars[VAR_MOUSE_Y_POS]);
} else {
element.var16 = 0;
element.var14 = 0;
@@ -1590,27 +1584,27 @@ void processSeqListElement(SeqListElement &element) {
&& !addAni(3, element.objIdx, ptr1, element, 0, &var_4)) || (element.var16 == 2 && !addAni(2, element.objIdx, ptr1, element, 0,
&var_4))) {
if (element.varC == 255) {
- globalVars[VAR_MOUSE_Y_POS] = 0;
+ g_cine->_globalVars[VAR_MOUSE_Y_POS] = 0;
}
}
if ((element.var14 == 1
&& !addAni(0, element.objIdx, ptr1, element, 1, &var_2))) {
if (element.varC == 255) {
- globalVars[VAR_MOUSE_X_POS] = 0;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = 0;
if (var_4 != -1) {
- objectTable[element.objIdx].costume = var_4;
+ g_cine->_objectTable[element.objIdx].costume = var_4;
}
}
}
if ((element.var14 == 2 && !addAni(1, element.objIdx, ptr1, element, 1, &var_2))) {
if (element.varC == 255) {
- globalVars[VAR_MOUSE_X_POS] = 0;
+ g_cine->_globalVars[VAR_MOUSE_X_POS] = 0;
if (var_4 != -1) {
- objectTable[element.objIdx].costume = var_4;
+ g_cine->_objectTable[element.objIdx].costume = var_4;
}
}
}
@@ -1618,7 +1612,7 @@ void processSeqListElement(SeqListElement &element) {
if (element.var16 + element.var14 == 0) {
if (element.var1C) {
if (element.var1E) {
- objectTable[element.objIdx].costume = 0;
+ g_cine->_objectTable[element.objIdx].costume = 0;
element.var1E = 0;
}
@@ -1633,7 +1627,7 @@ void processSeqListElement(SeqListElement &element) {
void processSeqList() {
Common::List<SeqListElement>::iterator it;
- for (it = seqList.begin(); it != seqList.end(); ++it) {
+ for (it = g_cine->_seqList.begin(); it != g_cine->_seqList.end(); ++it) {
if (it->var4 == -1) {
continue;
}
diff --git a/engines/cine/various.h b/engines/cine/various.h
index 3f362b1ad6..3a14328035 100644
--- a/engines/cine/various.h
+++ b/engines/cine/various.h
@@ -66,8 +66,6 @@ struct SeqListElement {
int16 var1E;
};
-extern Common::List<SeqListElement> seqList;
-
extern uint16 var2;
extern uint16 var3;
extern uint16 var4;
@@ -95,8 +93,6 @@ extern uint16 _messageLen;
extern int16 playerCommand;
-extern Common::String commandBuffer;
-
extern char currentPrcName[20];
extern char currentRelName[20];
extern char currentObjectName[20];
@@ -137,8 +133,6 @@ struct SelectedObjStruct {
};
#define NUM_MAX_ZONE 16
-extern Common::Array<uint16> zoneData;
-extern Common::Array<uint16> zoneQuery;
void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 param5);
diff --git a/engines/cruise/actor.cpp b/engines/cruise/actor.cpp
index 87b7f0a27f..9cbc3dd9ae 100644
--- a/engines/cruise/actor.cpp
+++ b/engines/cruise/actor.cpp
@@ -74,8 +74,8 @@ int flag_aff_chemin;
void getPixel(int x, int y) {
- for (uint i = 0; i < polyStructs->size(); ++i) {
- CtStruct &ct = (*polyStructs)[i];
+ for (uint i = 0; i < _vm->_polyStructs->size(); ++i) {
+ CtStruct &ct = (*_vm->_polyStructs)[i];
numPoly = ct.num;
if (walkboxState[numPoly] == 0 && ct.bounds.contains(x, y)) {
@@ -293,7 +293,7 @@ int point_proche(int16 table[][2]) {
int x1, y1, i, x, y, p;
int d1 = 1000;
- polyStructs = &CVars.polyStructNorm;
+ _vm->_polyStructs = &_vm->_polyStructNorm;
if (nclick_noeud == 1) {
x = x_mouse;
@@ -301,19 +301,19 @@ int point_proche(int16 table[][2]) {
x1 = table_ptselect[0][0];
y1 = table_ptselect[0][1];
- polyStructs = &CVars.polyStructExp;
+ _vm->_polyStructs = &_vm->_polyStructExp;
getPixel(x, y);
if (!flag_obstacle) {
- polyStructs = &CVars.polyStructNorm;
+ _vm->_polyStructs = &_vm->_polyStructNorm;
getPixel(x, y);
if (flag_obstacle) {
polydroite(x1, y1, x, y);
}
- polyStructs = &CVars.polyStructExp;
+ _vm->_polyStructs = &_vm->_polyStructExp;
}
if (!flag_obstacle) { /* dans flag_obstacle --> couleur du point */
x1 = table_ptselect[0][0];
@@ -325,7 +325,7 @@ int point_proche(int16 table[][2]) {
y_mouse = Y;
}
}
- polyStructs = &CVars.polyStructNorm;
+ _vm->_polyStructs = &_vm->_polyStructNorm;
p = -1;
for (i = 0; i < ctp_routeCoordCount; i++) {
@@ -453,7 +453,7 @@ void valide_noeud(int16 table[], int16 p, int *nclick, int16 solution0[20 + 3][2
table_ptselect[*nclick][0] = x_mouse;
table_ptselect[*nclick][1] = y_mouse;
(*nclick)++;
- polyStructs = &CVars.polyStructNorm;
+ _vm->_polyStructs = &_vm->_polyStructNorm;
if (*nclick == 2) { // second point
x1 = table_ptselect[0][0];
@@ -464,7 +464,7 @@ void valide_noeud(int16 table[], int16 p, int *nclick, int16 solution0[20 + 3][2
return;
}
flag_aff_chemin = 1;
- polyStructs = &CVars.polyStructExp;
+ _vm->_polyStructs = &_vm->_polyStructExp;
// can we go there directly ?
polydroite(x1, y1, x2, y2);
@@ -472,7 +472,7 @@ void valide_noeud(int16 table[], int16 p, int *nclick, int16 solution0[20 + 3][2
if (!flag_obstacle) {
solution0[0][0] = x1;
solution0[0][1] = y1;
- polyStructs = &CVars.polyStructExp;
+ _vm->_polyStructs = &_vm->_polyStructExp;
poly2(x2, y2, ctp_routeCoords[select_noeud[1]][0],
ctp_routeCoords[select_noeud[1]][1]);
@@ -516,7 +516,7 @@ void valide_noeud(int16 table[], int16 p, int *nclick, int16 solution0[20 + 3][2
solution0[++i][1] =
ctp_routeCoords[p1][1];
}
- polyStructs = &CVars.polyStructExp;
+ _vm->_polyStructs = &_vm->_polyStructExp;
poly2(x2, y2,
ctp_routeCoords[select_noeud[1]][0],
ctp_routeCoords[select_noeud[1]][1]);
@@ -541,7 +541,7 @@ void valide_noeud(int16 table[], int16 p, int *nclick, int16 solution0[20 + 3][2
while (flag_obstacle && i != d) {
x2 = solution0[i][0];
y2 = solution0[i][1];
- polyStructs = &CVars.polyStructExp;
+ _vm->_polyStructs = &_vm->_polyStructExp;
polydroite(x1, y1, x2, y2);
i--;
}
@@ -569,7 +569,7 @@ int16 computePathfinding(MovementEntry &moveInfo, int16 x, int16 y, int16 destX,
persoStruct *perso;
int num;
- if (!polyStruct) {
+ if (!_vm->_polyStruct) {
moveInfo.x = -1;
moveInfo.y = -1;
@@ -621,7 +621,7 @@ int16 computePathfinding(MovementEntry &moveInfo, int16 x, int16 y, int16 destX,
}
nclick_noeud = 0;
- polyStructs = &CVars.polyStructNorm;
+ _vm->_polyStructs = &_vm->_polyStructNorm;
flag_aff_chemin = 0;
if (x == destX && y == destY) {
diff --git a/engines/cruise/background.cpp b/engines/cruise/background.cpp
index da6a35aff0..3ac57cc376 100644
--- a/engines/cruise/background.cpp
+++ b/engines/cruise/background.cpp
@@ -47,10 +47,10 @@ int loadMEN(uint8 **ptr) {
if (!strcmp(localPtr, "MEN")) {
localPtr += 4;
- CVars.titleColor = *(localPtr++);
- CVars.selectColor = *(localPtr++);
- CVars.itemColor = *(localPtr++);
- CVars.subColor = *(localPtr++);
+ titleColor = *(localPtr++);
+ selectColor = *(localPtr++);
+ itemColor = *(localPtr++);
+ subColor = *(localPtr++);
*ptr = (uint8 *) localPtr;
@@ -104,9 +104,9 @@ int loadBackground(const char *name, int idx) {
backgroundChanged[idx] = true;
- ptrToFree = CVars.pPage10;
+ ptrToFree = gfxModuleData.pPage10;
if (loadFileSub1(&ptrToFree, name, NULL) < 0) {
- if (ptrToFree != CVars.pPage10)
+ if (ptrToFree != gfxModuleData.pPage10)
MemFree(ptrToFree);
return (-18);
diff --git a/engines/cruise/cruise.cpp b/engines/cruise/cruise.cpp
index 964f22af3d..3af77f3ef3 100644
--- a/engines/cruise/cruise.cpp
+++ b/engines/cruise/cruise.cpp
@@ -52,10 +52,6 @@ CruiseEngine *_vm;
CruiseEngine::CruiseEngine(OSystem * syst, const CRUISEGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
-#ifdef PALMOS_MODE
- _currentVolumeFile = new Common::File();
-#endif
-
DebugMan.addDebugChannel(kCruiseDebugScript, "scripts", "Scripts debug level");
DebugMan.addDebugChannel(kCruiseDebugSound, "sound", "Sound debug level");
@@ -137,8 +133,8 @@ void CruiseEngine::initialize() {
}
void CruiseEngine::deinitialise() {
- CVars.polyStructNorm.clear();
- CVars.polyStructExp.clear();
+ _vm->_polyStructNorm.clear();
+ _vm->_polyStructExp.clear();
// Clear any backgrounds
for (int i = 0; i < 8; ++i) {
@@ -205,7 +201,7 @@ void CruiseEngine::pauseEngine(bool pause) {
if (pause) {
// Draw the 'Paused' message
drawSolidBox(64, 100, 256, 117, 0);
- drawString(10, 100, langString(ID_PAUSED), CVars.pPage00, CVars.itemColor, 300);
+ drawString(10, 100, langString(ID_PAUSED), gfxModuleData.pPage00, itemColor, 300);
gfxModuleData_flipScreen();
_savedCursor = currentCursor;
diff --git a/engines/cruise/cruise.h b/engines/cruise/cruise.h
index 0428240167..94f5c68ca0 100644
--- a/engines/cruise/cruise.h
+++ b/engines/cruise/cruise.h
@@ -40,10 +40,10 @@
/**
* This is the namespace of the Cruise engine.
*
- * Status of this engine: ???
+ * Status of this engine: Game is completable, engine needs objectifying
*
* Supported games:
- * - ???
+ * - Cruise for a Corpse
*/
namespace Cruise {
@@ -112,6 +112,22 @@ public:
void initAllData();
Common::RandomSource _rnd;
+
+ Common::List<byte *> _memList;
+
+ typedef Common::List<Common::Rect> RectList;
+
+ RectList _dirtyRects;
+ RectList _priorFrameRects;
+
+ Common::File _currentVolumeFile;
+
+ Common::Array<CtStruct> _polyStructNorm;
+ Common::Array<CtStruct> _polyStructExp;
+ Common::Array<CtStruct> *_polyStructs;
+ Common::Array<CtStruct> *_polyStruct;
+
+ Common::File _PAL_file;
};
extern CruiseEngine *_vm;
diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp
index ceab9bf706..aa78f84e3d 100644
--- a/engines/cruise/cruise_main.cpp
+++ b/engines/cruise/cruise_main.cpp
@@ -38,15 +38,15 @@ namespace Cruise {
enum RelationType {RT_REL = 30, RT_MSG = 50};
static int playerDontAskQuit;
-#if !defined(__DS__)
-//unsigned int timer = 0;
-#endif
+unsigned int timer = 0;
+
+gfxEntryStruct* linkedMsgList = NULL;
void MemoryList() {
- if (!CVars.memList.empty()) {
+ if (!_vm->_memList.empty()) {
printf("Current list of un-freed memory blocks:\n");
Common::List<byte *>::iterator i;
- for (i = CVars.memList.begin(); i != CVars.memList.end(); ++i) {
+ for (i = _vm->_memList.begin(); i != _vm->_memList.end(); ++i) {
byte *v = *i;
printf("%s - %d\n", (const char *)(v - 68), *((int32 *)(v - 72)));
}
@@ -71,7 +71,7 @@ void *MemoryAlloc(uint32 size, bool clearFlag, int32 lineNum, const char *fname)
// Add the block to the memory list
result = v + 64 + 8;
- CVars.memList.push_back(result);
+ _vm->_memList.push_back(result);
} else
result = (byte *)malloc(size);
@@ -89,7 +89,7 @@ void MemoryFree(void *v) {
byte *p = (byte *)v;
assert(*((uint32 *) (p - 4)) == 0x41424344);
- CVars.memList.remove(p);
+ _vm->_memList.remove(p);
free(p - 8 - 64);
} else
free(v);
@@ -103,8 +103,8 @@ void drawBlackSolidBoxSmall() {
void loadPackedFileToMem(int fileIdx, uint8 *buffer) {
changeCursor(CURSOR_DISK);
- currentVolumeFile.seek(volumePtrToFileDescriptor[fileIdx].offset, SEEK_SET);
- currentVolumeFile.read(buffer, volumePtrToFileDescriptor[fileIdx].size);
+ _vm->_currentVolumeFile.seek(volumePtrToFileDescriptor[fileIdx].offset, SEEK_SET);
+ _vm->_currentVolumeFile.read(buffer, volumePtrToFileDescriptor[fileIdx].size);
}
int getNumObjectsByClass(int scriptIdx, int param) {
@@ -951,7 +951,7 @@ bool createDialog(int objOvl, int objIdx, int x, int y) {
int color;
if (objectState2 == -2)
- color = CVars.subColor;
+ color = subColor;
else
color = -1;
@@ -1388,12 +1388,12 @@ void closeAllMenu() {
freeMenu(menuTable[1]);
menuTable[1] = NULL;
}
- if (CVars.linkedMsgList) {
+ if (linkedMsgList) {
ASSERT(0);
-// freeMsgList(CVars.linkedMsgList);
+// freeMsgList(linkedMsgList);
}
- CVars.linkedMsgList = NULL;
+ linkedMsgList = NULL;
linkedRelation = NULL;
}
@@ -1543,12 +1543,12 @@ int CruiseEngine::processInput() {
freeMenu(menuTable[0]);
menuTable[0] = NULL;
- if (CVars.linkedMsgList) {
+ if (linkedMsgList) {
ASSERT(0);
- // freeMsgList(CVars.linkedMsgList);
+ // freeMsgList(linkedMsgList);
}
- CVars.linkedMsgList = NULL;
+ linkedMsgList = NULL;
linkedRelation = NULL;
changeCursor(CURSOR_NORMAL);
@@ -1580,10 +1580,10 @@ int CruiseEngine::processInput() {
menuTable[0] = NULL;
}
- if (CVars.linkedMsgList) {
-// freeMsgList(CVars.linkedMsgList);
+ if (linkedMsgList) {
+// freeMsgList(linkedMsgList);
}
- CVars.linkedMsgList = NULL;
+ linkedMsgList = NULL;
linkedRelation = NULL;
changeCursor(CURSOR_NORMAL);
} else { // call sub relation when clicking in inventory
@@ -1653,7 +1653,7 @@ int CruiseEngine::processInput() {
strcpy(text, menuTable[0]->stringPtr);
strcat(text, ":");
strcat(text, currentMenuElement->string);
- CVars.linkedMsgList = renderText(320, (const char *)text);
+ linkedMsgList = renderText(320, (const char *)text);
changeCursor(CURSOR_CROSS);
}
}
@@ -1831,19 +1831,17 @@ void CruiseEngine::mainLoop() {
if (!skipEvents)
skipEvents = manageEvents();
- if (playerDontAskQuit) break;
+ if (playerDontAskQuit)
+ break;
- if (_vm->getDebugger()->isAttached())
- _vm->getDebugger()->onFrame();
+ _vm->getDebugger()->onFrame();
} while (currentTick < lastTick + _gameSpeed);
} else {
manageEvents();
if (currentTick >= (lastTickDebug + 10)) {
lastTickDebug = currentTick;
-
- if (_vm->getDebugger()->isAttached())
- _vm->getDebugger()->onFrame();
+ _vm->getDebugger()->onFrame();
}
}
if (playerDontAskQuit)
diff --git a/engines/cruise/cruise_main.h b/engines/cruise/cruise_main.h
index 406ebfaa2b..8657b4bc21 100644
--- a/engines/cruise/cruise_main.h
+++ b/engines/cruise/cruise_main.h
@@ -81,7 +81,7 @@ enum ResType {
OBJ_TYPE_EXIT = 9
};
-//extern gfxEntryStruct* linkedMsgList;
+extern gfxEntryStruct* linkedMsgList;
extern int buttonDown;
extern int selectDown;
diff --git a/engines/cruise/ctp.cpp b/engines/cruise/ctp.cpp
index 1ee29917d1..4f6c21e0e4 100644
--- a/engines/cruise/ctp.cpp
+++ b/engines/cruise/ctp.cpp
@@ -23,14 +23,14 @@
*
*/
+#include "cruise/cruise.h"
#include "cruise/cruise_main.h"
#include "common/endian.h"
#include "common/util.h"
namespace Cruise {
-Common::Array<CtStruct> *polyStructs = NULL;
-Common::Array<CtStruct> *polyStruct = NULL;
+uint8 *ctpVar17;
int currentWalkBoxCenterX;
int currentWalkBoxCenterY;
@@ -320,16 +320,16 @@ int initCt(const char *ctpName) {
// Load the polyStructNorm list
for (int i = numberOfWalkboxes - 1; i >= 0; i--) {
- makeCtStruct(CVars.polyStructNorm, ctp_walkboxTable, i, 0);
+ makeCtStruct(_vm->_polyStructNorm, ctp_walkboxTable, i, 0);
}
// Load the polyStructExp list
for (int i = numberOfWalkboxes - 1; i >= 0; i--) {
- makeCtStruct(CVars.polyStructExp, ctp_walkboxTable, i, walkboxZoom[i] * 20);
+ makeCtStruct(_vm->_polyStructExp, ctp_walkboxTable, i, walkboxZoom[i] * 20);
}
- polyStruct = polyStructs = &CVars.polyStructNorm;
+ _vm->_polyStruct = _vm->_polyStructs = &_vm->_polyStructNorm;
return (1);
}
diff --git a/engines/cruise/ctp.h b/engines/cruise/ctp.h
index f6968e184a..3c6c9582cc 100644
--- a/engines/cruise/ctp.h
+++ b/engines/cruise/ctp.h
@@ -62,8 +62,7 @@ public:
Common::Array<CtEntry> slices;
};
-extern Common::Array<CtStruct> *polyStructs;
-extern Common::Array<CtStruct> *polyStruct;
+extern uint8 *ctpVar17;
int initCt(const char * ctpName);
int computeDistance(int varX, int varY, int paramX, int paramY);
diff --git a/engines/cruise/decompiler.cpp b/engines/cruise/decompiler.cpp
index ba4ade56a7..31d9dcef9b 100644
--- a/engines/cruise/decompiler.cpp
+++ b/engines/cruise/decompiler.cpp
@@ -760,8 +760,6 @@ int decompFunction() {
char *var1;
char *objIdxStr;
char *ovlStr;
- char varName[256];
- int i;
var1 = popDecomp();
objIdxStr = popDecomp();
diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp
index e1f12b734e..e43fadf598 100644
--- a/engines/cruise/detection.cpp
+++ b/engines/cruise/detection.cpp
@@ -237,7 +237,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI
+ Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class CruiseMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/cruise/font.cpp b/engines/cruise/font.cpp
index aecadab193..3a609cb8d4 100644
--- a/engines/cruise/font.cpp
+++ b/engines/cruise/font.cpp
@@ -30,7 +30,6 @@
#include "cruise/cruise_main.h"
#include "cruise/mouse.h"
#include "cruise/staticres.h"
-#include "cruise/vars.h"
namespace Cruise {
@@ -99,7 +98,7 @@ int32 getTextLineCount(int32 rightBorder_X, int16 wordSpacingWidth,
void loadFNT(const char *fileName) {
uint8 header[4];
- CVars._systemFNT = NULL;
+ _systemFNT = NULL;
Common::File fontFileHandle;
@@ -113,20 +112,20 @@ void loadFNT(const char *fileName) {
if (strcmp((char*)header, "FNT") == 0) {
uint32 fontSize = fontFileHandle.readUint32BE();
- CVars._systemFNT = (uint8 *)mallocAndZero(fontSize);
+ _systemFNT = (uint8 *)mallocAndZero(fontSize);
- if (CVars._systemFNT != NULL) {
+ if (_systemFNT != NULL) {
fontFileHandle.seek(4);
- fontFileHandle.read(CVars._systemFNT, fontSize);
+ fontFileHandle.read(_systemFNT, fontSize);
// Flip structure values from BE to LE for font files - this is for consistency
// with font resources, which are in LE formatt
- FontInfo *f = (FontInfo *)CVars._systemFNT;
+ FontInfo *f = (FontInfo *)_systemFNT;
bigEndianLongToNative(&f->offset);
bigEndianLongToNative(&f->size);
flipGen(&f->numChars, 6); // numChars, hSpacing, and vSpacing
- FontEntry *fe = (FontEntry *)(CVars._systemFNT + sizeof(FontInfo));
+ FontEntry *fe = (FontEntry *)(_systemFNT + sizeof(FontInfo));
for (int i = 0; i < f->numChars; ++i, ++fe) {
bigEndianLongToNative(&fe->offset); // Flip 32-bit offset field
@@ -141,10 +140,10 @@ void loadFNT(const char *fileName) {
void initSystem() {
int32 i;
- CVars.itemColor = 15;
- CVars.titleColor = 9;
- CVars.selectColor = 13;
- CVars.subColor = 10;
+ itemColor = 15;
+ titleColor = 9;
+ selectColor = 13;
+ subColor = 10;
for (i = 0; i < 64; i++) {
strcpy(preloadData[i].name, "");
@@ -170,7 +169,7 @@ void initSystem() {
}
void freeSystem() {
- MemFree(CVars._systemFNT);
+ MemFree(_systemFNT);
}
void bigEndianShortToNative(void *var) {
@@ -310,10 +309,10 @@ gfxEntryStruct *renderText(int inRightBorder_X, const char *string) {
fontPtr = (const FontInfo *)filesDatabase[fontFileIndex].subData.ptr;
if (!fontPtr) {
- fontPtr = (const FontInfo *)CVars._systemFNT;
+ fontPtr = (const FontInfo *)_systemFNT;
}
} else {
- fontPtr = (const FontInfo *)CVars._systemFNT;
+ fontPtr = (const FontInfo *)_systemFNT;
}
if (!fontPtr) {
diff --git a/engines/cruise/function.cpp b/engines/cruise/function.cpp
index a25a54c562..3d07abf441 100644
--- a/engines/cruise/function.cpp
+++ b/engines/cruise/function.cpp
@@ -446,8 +446,8 @@ int16 Op_KillMenu() {
}
// Free the message list
-// if (CVars.linkedMsgList) freeMsgList(CVars.linkedMsgList);
- CVars.linkedMsgList = NULL;
+// if (linkedMsgList) freeMsgList(linkedMsgList);
+ linkedMsgList = NULL;
linkedRelation = NULL;
return 0;
@@ -1268,10 +1268,10 @@ int16 Op_regenerateBackgroundIncrust() {
int16 Op_SetStringColors() {
// TODO: here ignore if low color mode
- CVars.subColor = (uint8) popVar();
- CVars.itemColor = (uint8) popVar();
- CVars.selectColor = (uint8) popVar();
- CVars.titleColor = (uint8) popVar();
+ subColor = (uint8) popVar();
+ itemColor = (uint8) popVar();
+ selectColor = (uint8) popVar();
+ titleColor = (uint8) popVar();
return 0;
}
diff --git a/engines/cruise/gfxModule.cpp b/engines/cruise/gfxModule.cpp
index 1e77037e84..4a88e6ed5e 100644
--- a/engines/cruise/gfxModule.cpp
+++ b/engines/cruise/gfxModule.cpp
@@ -34,16 +34,25 @@
namespace Cruise {
-typedef Common::List<Common::Rect> RectList;
+uint8 page00[320 * 200];
+uint8 page10[320 * 200];
-/*gfxModuleDataStruct gfxModuleData = {
+char screen[320 * 200];
+palEntry lpalette[256];
+
+int palDirtyMin = 256;
+int palDirtyMax = -1;
+
+bool _dirtyRectScreen = false;
+
+gfxModuleDataStruct gfxModuleData = {
0, // use Tandy
0, // use EGA
1, // use VGA
- CVars.page00, // pPage00
- CVars.page10, // pPage10
-};*/
+ page00, // pPage00
+ page10, // pPage10
+};
void gfxModuleData_gfxClearFrameBuffer(uint8 *ptr) {
memset(ptr, 0, 64000);
@@ -99,16 +108,16 @@ void convertGfxFromMode5(const uint8 *sourcePtr, int width, int height, uint8 *d
}
void gfxModuleData_setDirtyColors(int min, int max) {
- if (min < CVars.palDirtyMin)
- CVars.palDirtyMin = min;
- if (max > CVars.palDirtyMax)
- CVars.palDirtyMax = max;
+ if (min < palDirtyMin)
+ palDirtyMin = min;
+ if (max > palDirtyMax)
+ palDirtyMax = max;
}
void gfxModuleData_setPalColor(int idx, int r, int g, int b) {
- CVars.lpalette[idx].R = r;
- CVars.lpalette[idx].G = g;
- CVars.lpalette[idx].B = b;
+ lpalette[idx].R = r;
+ lpalette[idx].G = g;
+ lpalette[idx].B = b;
gfxModuleData_setDirtyColors(idx, idx);
}
@@ -120,10 +129,10 @@ void gfxModuleData_setPalEntries(const byte *ptr, int start, int num) {
G = *(ptr++);
B = *(ptr++);
- CVars.lpalette[i].R = R;
- CVars.lpalette[i].G = G;
- CVars.lpalette[i].B = B;
- CVars.lpalette[i].A = 255;
+ lpalette[i].R = R;
+ lpalette[i].G = G;
+ lpalette[i].B = B;
+ lpalette[i].A = 255;
}
gfxModuleData_setDirtyColors(start, start + num - 1);
@@ -156,10 +165,10 @@ void gfxModuleData_setPal256(const byte *ptr) {
if (B > 0xFF)
B = 0xFF;
- CVars.lpalette[i].R = R;
- CVars.lpalette[i].G = G;
- CVars.lpalette[i].B = B;
- CVars.lpalette[i].A = 255;
+ lpalette[i].R = R;
+ lpalette[i].G = G;
+ lpalette[i].B = B;
+ lpalette[i].A = 255;
}
gfxModuleData_setDirtyColors(0, 16);
@@ -214,18 +223,18 @@ void gfxCopyRect(const uint8 *sourceBuffer, int width, int height, byte *dest, i
void gfxModuleData_Init() {
memset(globalScreen, 0, 320 * 200);
- memset(CVars.page00, 0, 320 * 200);
- memset(CVars.page10, 0, 320 * 200);
+ memset(page00, 0, 320 * 200);
+ memset(page10, 0, 320 * 200);
}
void gfxModuleData_flipScreen() {
- memcpy(globalScreen, CVars.pPage00, 320 * 200);
+ memcpy(globalScreen, gfxModuleData.pPage00, 320 * 200);
flip();
}
void gfxModuleData_addDirtyRect(const Common::Rect &r) {
- CVars._dirtyRects.push_back(Common::Rect( MAX(r.left, (int16)0), MAX(r.top, (int16)0),
+ _vm->_dirtyRects.push_back(Common::Rect( MAX(r.left, (int16)0), MAX(r.top, (int16)0),
MIN(r.right, (int16)320), MIN(r.bottom, (int16)200)));
}
@@ -242,11 +251,11 @@ static bool unionRectangle(Common::Rect &pDest, const Common::Rect &pSrc1, const
}
static void mergeClipRects() {
- RectList::iterator rOuter, rInner;
+ CruiseEngine::RectList::iterator rOuter, rInner;
- for (rOuter = CVars._dirtyRects.begin(); rOuter != CVars._dirtyRects.end(); ++rOuter) {
+ for (rOuter = _vm->_dirtyRects.begin(); rOuter != _vm->_dirtyRects.end(); ++rOuter) {
rInner = rOuter;
- while (++rInner != CVars._dirtyRects.end()) {
+ while (++rInner != _vm->_dirtyRects.end()) {
if ((*rOuter).intersects(*rInner)) {
// these two rectangles overlap, so translate it to a bigger rectangle
@@ -254,7 +263,7 @@ static void mergeClipRects() {
unionRectangle(*rOuter, *rOuter, *rInner);
// remove the inner rect from the list
- CVars._dirtyRects.erase(rInner);
+ _vm->_dirtyRects.erase(rInner);
// move back to beginning of list
rInner = rOuter;
@@ -266,16 +275,16 @@ static void mergeClipRects() {
void gfxModuleData_updatePalette() {
byte paletteRGBA[256 * 4];
- if (CVars.palDirtyMax != -1) {
- for (int i = CVars.palDirtyMin; i <= CVars.palDirtyMax; i++) {
- paletteRGBA[i * 4 + 0] = CVars.lpalette[i].R;
- paletteRGBA[i * 4 + 1] = CVars.lpalette[i].G;
- paletteRGBA[i * 4 + 2] = CVars.lpalette[i].B;
+ if (palDirtyMax != -1) {
+ for (int i = palDirtyMin; i <= palDirtyMax; i++) {
+ paletteRGBA[i * 4 + 0] = lpalette[i].R;
+ paletteRGBA[i * 4 + 1] = lpalette[i].G;
+ paletteRGBA[i * 4 + 2] = lpalette[i].B;
paletteRGBA[i * 4 + 3] = 0xFF;
}
- g_system->setPalette(paletteRGBA + CVars.palDirtyMin*4, CVars.palDirtyMin, CVars.palDirtyMax - CVars.palDirtyMin + 1);
- CVars.palDirtyMin = 256;
- CVars.palDirtyMax = -1;
+ g_system->setPalette(paletteRGBA + palDirtyMin*4, palDirtyMin, palDirtyMax - palDirtyMin + 1);
+ palDirtyMin = 256;
+ palDirtyMax = -1;
}
}
@@ -285,32 +294,32 @@ void gfxModuleData_updateScreen() {
}
void flip() {
- RectList::iterator dr;
+ CruiseEngine::RectList::iterator dr;
// Update the palette
gfxModuleData_updatePalette();
// Make a copy of the prior frame's dirty rects, and then backup the current frame's rects
- RectList tempList = CVars._priorFrameRects;
- CVars._priorFrameRects = CVars._dirtyRects;
+ CruiseEngine::RectList tempList = _vm->_priorFrameRects;
+ _vm->_priorFrameRects = _vm->_dirtyRects;
// Merge the prior frame's dirty rects into the current frame's list
for (dr = tempList.begin(); dr != tempList.end(); ++dr) {
Common::Rect &r = *dr;
- CVars._dirtyRects.push_back(Common::Rect(r.left, r.top, r.right, r.bottom));
+ _vm->_dirtyRects.push_back(Common::Rect(r.left, r.top, r.right, r.bottom));
}
// Merge any overlapping rects to simplify the drawing process
mergeClipRects();
// Copy any modified areas
- for (dr = CVars._dirtyRects.begin(); dr != CVars._dirtyRects.end(); ++dr) {
+ for (dr = _vm->_dirtyRects.begin(); dr != _vm->_dirtyRects.end(); ++dr) {
Common::Rect &r = *dr;
g_system->copyRectToScreen(globalScreen + 320 * r.top + r.left, 320,
r.left, r.top, r.width(), r.height());
}
- CVars._dirtyRects.clear();
+ _vm->_dirtyRects.clear();
// Allow the screen to update
g_system->updateScreen();
@@ -318,7 +327,7 @@ void flip() {
void drawSolidBox(int32 x1, int32 y1, int32 x2, int32 y2, uint8 colour) {
for (int y = y1; y < y2; ++y) {
- byte *p = &CVars.pPage00[y * 320 + x1];
+ byte *p = &gfxModuleData.pPage00[y * 320 + x1];
Common::set_to(p, p + (x2 - x1), colour);
}
}
@@ -332,7 +341,7 @@ void resetBitmap(uint8 *dataPtr, int32 dataSize) {
* to figure out rectangles of changed areas for dirty rectangles
*/
void switchBackground(const byte *newBg) {
- const byte *bg = CVars.pPage00;
+ const byte *bg = gfxModuleData.pPage00;
int sliceXStart, sliceXEnd;
// If both the upper corners are different, presume it's a full screen change
diff --git a/engines/cruise/gfxModule.h b/engines/cruise/gfxModule.h
index 289cc24d98..1dbc5afc9b 100644
--- a/engines/cruise/gfxModule.h
+++ b/engines/cruise/gfxModule.h
@@ -28,14 +28,14 @@
namespace Cruise {
-/*struct gfxModuleDataStruct {
+struct gfxModuleDataStruct {
int useTandy;
int useEGA;
int useVGA;
uint8 *pPage00;
uint8 *pPage10;
-};*/
+};
struct palEntry {
uint8 R;
@@ -44,7 +44,7 @@ struct palEntry {
uint8 A;
};
-//extern gfxModuleDataStruct gfxModuleData;
+extern gfxModuleDataStruct gfxModuleData;
void gfxModuleData_gfxClearFrameBuffer(uint8 *ptr);
void gfxModuleData_setDirtyColors(int min, int max);
diff --git a/engines/cruise/mainDraw.cpp b/engines/cruise/mainDraw.cpp
index 67c6d96455..aaa6f987b6 100644
--- a/engines/cruise/mainDraw.cpp
+++ b/engines/cruise/mainDraw.cpp
@@ -135,7 +135,7 @@ void flipScreen() {
gfxModuleData_setPal256(workpal);
}
- SWAP(CVars.pPage00, CVars.pPage10);
+ SWAP(gfxModuleData.pPage00, gfxModuleData.pPage10);
gfxModuleData_flipScreen();
@@ -1330,7 +1330,7 @@ void drawMenu(menuStruct *pMenu) {
int wx = x + (nbcol - 1) * (160 / 2);
if (wx <= 320 - 160) {
- drawMessage(pMenu->gfx, wx, y - hline, 160, CVars.titleColor, CVars.pPage10);
+ drawMessage(pMenu->gfx, wx, y - hline, 160, titleColor, gfxModuleData.pPage10);
}
wx = x;
@@ -1348,17 +1348,17 @@ void drawMenu(menuStruct *pMenu) {
int color;
if (p1->selected) {
- color = CVars.selectColor;
+ color = selectColor;
} else {
if (p1->color != 255) {
color = p1->color;
} else {
- color = CVars.itemColor;
+ color = itemColor;
}
}
if (wx <= (320 - 160)) {
- drawMessage(p2, wx, wy, 160, color, CVars.pPage10);
+ drawMessage(p2, wx, wy, 160, color, gfxModuleData.pPage10);
}
wy += hline;
@@ -1418,7 +1418,7 @@ void mainDraw(int16 param) {
bgPtr = backgroundScreens[masterScreen];
if (bgPtr) {
- gfxModuleData_gfxCopyScreen(bgPtr, CVars.pPage10);
+ gfxModuleData_gfxCopyScreen(bgPtr, gfxModuleData.pPage10);
if (backgroundChanged[masterScreen]) {
backgroundChanged[masterScreen] = false;
switchBackground(bgPtr);
@@ -1469,7 +1469,7 @@ void mainDraw(int16 param) {
if ((params.state >= 0) && (objZ2 >= 0) && filesDatabase[objZ2].subData.ptr) {
if (filesDatabase[objZ2].subData.resourceType == 8) { // Poly
- mainDrawPolygons(objZ2, currentObjPtr, objX2, params.scale, objY2, (char *)CVars.pPage10, (char *)filesDatabase[objZ2].subData.ptr); // poly
+ mainDrawPolygons(objZ2, currentObjPtr, objX2, params.scale, objY2, (char *)gfxModuleData.pPage10, (char *)filesDatabase[objZ2].subData.ptr); // poly
} else if (filesDatabase[objZ2].subData.resourceType == OBJ_TYPE_SOUND) {
} else if (filesDatabase[objZ2].resType == OBJ_TYPE_MASK) {
} else if (filesDatabase[objZ2].subData.resourceType == OBJ_TYPE_SPRITE) {
@@ -1477,7 +1477,7 @@ void mainDraw(int16 param) {
spriteHeight = filesDatabase[objZ2].height; // height
if (filesDatabase[objZ2].subData.ptr) {
- drawSprite(objX1, spriteHeight, currentObjPtr, filesDatabase[objZ2].subData.ptr, objY2, objX2, CVars.pPage10, filesDatabase[objZ2].subData.ptrMask);
+ drawSprite(objX1, spriteHeight, currentObjPtr, filesDatabase[objZ2].subData.ptr, objY2, objX2, gfxModuleData.pPage10, filesDatabase[objZ2].subData.ptrMask);
}
}
}
@@ -1573,7 +1573,7 @@ void mainDraw(int16 param) {
while (currentObjPtr) {
if (currentObjPtr->type == OBJ_TYPE_MESSAGE && currentObjPtr->freeze == 0) {
- drawMessage(currentObjPtr->gfxPtr, currentObjPtr->x, currentObjPtr->field_C, currentObjPtr->spriteIdx, currentObjPtr->color, CVars.pPage10);
+ drawMessage(currentObjPtr->gfxPtr, currentObjPtr->x, currentObjPtr->field_C, currentObjPtr->spriteIdx, currentObjPtr->color, gfxModuleData.pPage10);
isMessage = 1;
}
currentObjPtr = currentObjPtr->next;
@@ -1586,16 +1586,16 @@ void mainDraw(int16 param) {
drawMenu(menuTable[currentActiveMenu]);
return;
}
- } else if ((linkedRelation) && (CVars.linkedMsgList)) {
+ } else if ((linkedRelation) && (linkedMsgList)) {
int16 mouseX;
int16 mouseY;
int16 button;
getMouseStatus(&main10, &mouseX, &button, &mouseY);
- if (mouseY > (CVars.linkedMsgList->height)*2)
- drawMessage(CVars.linkedMsgList, 0, 0, 320, findHighColor(), CVars.pPage10);
+ if (mouseY > (linkedMsgList->height)*2)
+ drawMessage(linkedMsgList, 0, 0, 320, findHighColor(), gfxModuleData.pPage10);
else
- drawMessage(CVars.linkedMsgList, 0, 200, 320, findHighColor(), CVars.pPage10);
+ drawMessage(linkedMsgList, 0, 200, 320, findHighColor(), gfxModuleData.pPage10);
}
}
diff --git a/engines/cruise/perso.cpp b/engines/cruise/perso.cpp
index 1e92b8bc3e..e86daa5bef 100644
--- a/engines/cruise/perso.cpp
+++ b/engines/cruise/perso.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "cruise/cruise.h"
#include "cruise/cruise_main.h"
#include "common/util.h"
@@ -45,14 +46,14 @@ void freeCTP() {
freePerso(i);
}
- if (polyStruct) {
- CVars.polyStructNorm.clear();
- CVars.polyStructExp.clear();
- polyStruct = NULL;
+ if (_vm->_polyStruct) {
+ _vm->_polyStructNorm.clear();
+ _vm->_polyStructExp.clear();
+ _vm->_polyStruct = NULL;
}
- CVars.ctpVar17 = NULL;
- polyStruct = NULL;
+ ctpVar17 = NULL;
+ _vm->_polyStruct = NULL;
strcpy((char *)currentCtpName, "");
}
diff --git a/engines/cruise/saveload.cpp b/engines/cruise/saveload.cpp
index bfbf808168..24ea2facfe 100644
--- a/engines/cruise/saveload.cpp
+++ b/engines/cruise/saveload.cpp
@@ -113,10 +113,10 @@ static void syncBasicInfo(Common::Serializer &s) {
s.syncAsSint16LE(isMessage);
s.syncAsSint16LE(fadeFlag);
s.syncAsSint16LE(automaticMode);
- s.syncAsSint16LE(CVars.titleColor);
- s.syncAsSint16LE(CVars.itemColor);
- s.syncAsSint16LE(CVars.selectColor);
- s.syncAsSint16LE(CVars.subColor);
+ s.syncAsSint16LE(titleColor);
+ s.syncAsSint16LE(itemColor);
+ s.syncAsSint16LE(selectColor);
+ s.syncAsSint16LE(subColor);
s.syncAsSint16LE(narratorOvl);
s.syncAsSint16LE(narratorIdx);
s.syncAsSint16LE(aniX);
@@ -579,10 +579,10 @@ static void syncPerso(Common::Serializer &s, persoStruct &p) {
}
static void syncCT(Common::Serializer &s) {
- int v = (polyStruct) ? 1 : 0;
+ int v = (_vm->_polyStruct) ? 1 : 0;
s.syncAsSint32LE(v);
if (s.isLoading())
- polyStruct = (v != 0) ? &CVars.polyStructNorm : NULL;
+ _vm->_polyStruct = (v != 0) ? &_vm->_polyStructNorm : NULL;
if (v == 0)
// There is no further data to load or save
@@ -762,10 +762,10 @@ void initVars() {
// video param (vga and mcga mode)
- CVars.titleColor = 2;
- CVars.itemColor = 1;
- CVars.selectColor = 3;
- CVars.subColor = 5;
+ titleColor = 2;
+ itemColor = 1;
+ selectColor = 3;
+ subColor = 5;
//
diff --git a/engines/cruise/script.cpp b/engines/cruise/script.cpp
index f2877a7730..d6c1aa47f3 100644
--- a/engines/cruise/script.cpp
+++ b/engines/cruise/script.cpp
@@ -618,13 +618,13 @@ int executeScripts(scriptInstanceStruct *ptr) {
positionInStack = 0;
do {
-#ifdef SKIP_INTRO
+//#ifdef SKIP_INTRO
if (currentScriptPtr->scriptOffset == 290
&& currentScriptPtr->overlayNumber == 4
&& currentScriptPtr->scriptNumber == 0) {
currentScriptPtr->scriptOffset = 923;
}
-#endif
+//#endif
opcodeType = getByteFromScript();
debugC(5, kCruiseDebugScript, "Script %s/%d ip=%d opcode=%d",
diff --git a/engines/cruise/vars.cpp b/engines/cruise/vars.cpp
index 87f0626355..c61cedc503 100644
--- a/engines/cruise/vars.cpp
+++ b/engines/cruise/vars.cpp
@@ -24,36 +24,15 @@
*/
#include "cruise/cruise_main.h"
-#include "cruise/vars.h"
-
-DECLARE_SINGLETON(Cruise::CruiseVars)
namespace Cruise {
-CruiseVars::CruiseVars() {
- _systemFNT = NULL;
-
- itemColor = 1;
- selectColor = 3;
- titleColor = 2;
- subColor = 5;
-
- linkedMsgList = NULL;
-
- palDirtyMin = 256;
- palDirtyMax = -1;
+uint8 *_systemFNT = NULL;
- _dirtyRectScreen = false;
-
- useTandy = 0;
- useEGA = 0;
- useVGA = 1;
-
- pPage00 = page00;
- pPage10 = page10;
-}
-
-CruiseVars::~CruiseVars() {}
+uint8 itemColor = 1;
+uint8 selectColor = 3;
+uint8 titleColor = 2;
+uint8 subColor = 5;
int16 lowMemory;
int16 scroll;
@@ -85,12 +64,6 @@ int16 autoTrack;
int16 currentDiskNumber = 1;
-#ifdef PALMOS_MODE
-Common::File *_currentVolumeFile;
-#else
-Common::File currentVolumeFile;
-#endif
-
int16 volumeNumEntry;
fileEntry *volumePtrToFileDescriptor = NULL;
diff --git a/engines/cruise/vars.h b/engines/cruise/vars.h
index 06b479128b..af39993f2f 100644
--- a/engines/cruise/vars.h
+++ b/engines/cruise/vars.h
@@ -27,16 +27,9 @@
#define CRUISE_VARS_H
#include "common/file.h"
-#include "common/list.h"
-#include "common/singleton.h"
-#include "common/endian.h"
-#include "common/util.h"
-#include "gfxModule.h"
namespace Cruise {
-typedef Common::List<Common::Rect> RectList;
-
#define NBCOLORS 256
#define NBSCREENS 8
@@ -61,50 +54,13 @@ struct menuElementStruct {
typedef int32(*opcodeTypeFunction)();
typedef int16(*opcodeFunction)();
+extern uint8 *_systemFNT;
extern int16 fontFileIndex;
-class CruiseVars : public Common::Singleton<CruiseVars> {
-public:
- uint8 *_systemFNT;
-
- uint8 itemColor;
- uint8 selectColor;
- uint8 titleColor;
- uint8 subColor;
-
- gfxEntryStruct* linkedMsgList;
-
- Common::List<byte *> memList;
-
- uint8 *ctpVar17;
-
- Common::Array<CtStruct> polyStructNorm;
- Common::Array<CtStruct> polyStructExp;
-
- uint8 page00[320 * 200];
- uint8 page10[320 * 200];
- char screen[320 * 200];
-
- palEntry lpalette[256];
-
- int palDirtyMin;
- int palDirtyMax;
-
- RectList _dirtyRects;
- RectList _priorFrameRects;
- bool _dirtyRectScreen;
-
- int useTandy;
- int useEGA;
- int useVGA;
-
- uint8 *pPage00;
- uint8 *pPage10;
-
- ~CruiseVars();
- CruiseVars();
-
-};
+extern uint8 itemColor;
+extern uint8 selectColor;
+extern uint8 titleColor;
+extern uint8 subColor;
extern int16 lowMemory;
extern int16 scroll;
@@ -211,13 +167,6 @@ extern int16 autoTrack;
extern int16 currentDiskNumber;
-#ifdef PALMOS_MODE
-extern Common::File *_currentVolumeFile;
-#define currentVolumeFile (*_currentVolumeFile)
-#else
-extern Common::File currentVolumeFile;
-#endif
-
extern int16 volumeNumEntry;
extern fileEntry *volumePtrToFileDescriptor;
@@ -345,6 +294,4 @@ extern uint8 globalScreen[320 * 200];
} // End of namespace Cruise
-#define CVars (::Cruise::CruiseVars::instance())
-
#endif
diff --git a/engines/cruise/volume.cpp b/engines/cruise/volume.cpp
index 86b37bb394..5535d5a016 100644
--- a/engines/cruise/volume.cpp
+++ b/engines/cruise/volume.cpp
@@ -23,13 +23,11 @@
*
*/
+#include "cruise/cruise.h"
#include "cruise/cruise_main.h"
namespace Cruise {
-#if !defined(__DS__)
-Common::File PAL_file;
-#endif
uint8 *PAL_ptr = NULL;
int16 numLoadedPal;
@@ -42,26 +40,25 @@ void loadPal(volumeDataStruct *entry) {
#if 0
char name[20];
- if (PAL_file.isOpen())
- PAL_file.close();
+ if (_vm->_PAL_file.isOpen())
+ _vm->_PAL_file.close();
removeExtention(entry->ident, name);
strcat(name, ".PAL");
- if (!PAL_file.open(name))
+ if (!_vm->_PAL_file.open(name))
return;
- numLoadedPal = PAL_file.readSint16BE();
- fileData2 = PAL_file.readSint16BE();
+ numLoadedPal = _vm->_PAL_file.readSint16BE();
+ fileData2 = _vm->_PAL_file.readSint16BE();
PAL_ptr = (uint8 *)MemAlloc(numLoadedPal * fileData2);
#endif
}
void closePal() {
-#if !defined(__DS__)
- if (PAL_file.isOpen()) {
- PAL_file.close();
+ if (_vm->_PAL_file.isOpen()) {
+ _vm->_PAL_file.close();
MemFree(PAL_ptr);
PAL_ptr = NULL;
@@ -69,22 +66,21 @@ void closePal() {
numLoadedPal = 0;
fileData2 = 0;
}
-#endif
}
int closeBase() {
- if (currentVolumeFile.isOpen()) {
- currentVolumeFile.close();
+ if (_vm->_currentVolumeFile.isOpen()) {
+ _vm->_currentVolumeFile.close();
MemFree(volumePtrToFileDescriptor);
strcpy(currentBaseName, "");
}
-#if !defined(__DS__)
- if (PAL_file.isOpen()) {
+
+ if (_vm->_PAL_file.isOpen()) {
closePal();
}
-#endif
+
return 0;
}
@@ -95,7 +91,7 @@ int getVolumeDataEntry(volumeDataStruct *entry) {
volumeNumEntry = 0;
volumeNumberOfEntry = 0;
- if (currentVolumeFile.isOpen()) {
+ if (_vm->_currentVolumeFile.isOpen()) {
freeDisk();
}
@@ -103,16 +99,16 @@ int getVolumeDataEntry(volumeDataStruct *entry) {
strcpy(buffer, entry->ident);
- currentVolumeFile.open(buffer);
+ _vm->_currentVolumeFile.open(buffer);
- if (!currentVolumeFile.isOpen()) {
+ if (!_vm->_currentVolumeFile.isOpen()) {
return (-14);
}
changeCursor(CURSOR_DISK);
- volumeNumberOfEntry = currentVolumeFile.readSint16BE();
- volumeSizeOfEntry = currentVolumeFile.readSint16BE();
+ volumeNumberOfEntry = _vm->_currentVolumeFile.readSint16BE();
+ volumeSizeOfEntry = _vm->_currentVolumeFile.readSint16BE();
volumeNumEntry = volumeNumberOfEntry;
@@ -129,11 +125,11 @@ int getVolumeDataEntry(volumeDataStruct *entry) {
}
for (i = 0; i < volumeNumEntry; i++) {
- currentVolumeFile.read(&volumePtrToFileDescriptor[i].name, 14);
- volumePtrToFileDescriptor[i].offset = currentVolumeFile.readSint32BE();
- volumePtrToFileDescriptor[i].size = currentVolumeFile.readSint32BE();
- volumePtrToFileDescriptor[i].extSize = currentVolumeFile.readSint32BE();
- volumePtrToFileDescriptor[i].unk3 = currentVolumeFile.readSint32BE();
+ _vm->_currentVolumeFile.read(&volumePtrToFileDescriptor[i].name, 14);
+ volumePtrToFileDescriptor[i].offset = _vm->_currentVolumeFile.readSint32BE();
+ volumePtrToFileDescriptor[i].size = _vm->_currentVolumeFile.readSint32BE();
+ volumePtrToFileDescriptor[i].extSize = _vm->_currentVolumeFile.readSint32BE();
+ volumePtrToFileDescriptor[i].unk3 = _vm->_currentVolumeFile.readSint32BE();
}
strcpy(currentBaseName, entry->ident);
@@ -182,8 +178,8 @@ int32 findFileInDisksSub1(const char *fileName) {
}
void freeDisk() {
- if (currentVolumeFile.isOpen()) {
- currentVolumeFile.close();
+ if (_vm->_currentVolumeFile.isOpen()) {
+ _vm->_currentVolumeFile.close();
MemFree(volumePtrToFileDescriptor);
}
@@ -198,7 +194,7 @@ void freeDisk() {
int16 findFileInList(char *fileName) {
int i;
- if (!currentVolumeFile.isOpen()) {
+ if (!_vm->_currentVolumeFile.isOpen()) {
return (-1);
}
@@ -252,7 +248,7 @@ int16 findFileInDisks(const char *name) {
if (!volumeDataLoaded) {
debug(1, "CNF wasn't loaded, reading now...");
- if (currentVolumeFile.isOpen()) {
+ if (_vm->_currentVolumeFile.isOpen()) {
askDisk(-1);
freeDisk();
}
@@ -261,7 +257,7 @@ int16 findFileInDisks(const char *name) {
readVolCnf();
}
- if (currentVolumeFile.isOpen()) {
+ if (_vm->_currentVolumeFile.isOpen()) {
askDisk(-1);
}
@@ -278,7 +274,7 @@ int16 findFileInDisks(const char *name) {
debug(1, "File found on disk %d", disk);
- if (currentVolumeFile.isOpen()) {
+ if (_vm->_currentVolumeFile.isOpen()) {
askDisk(-1);
}
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index 73ba591b4b..2397a474c6 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -28,6 +28,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "common/events.h"
+#include "common/translation.h"
#include "graphics/scaler.h"
@@ -35,8 +36,9 @@
#include "gui/GuiManager.h"
#include "gui/launcher.h"
#include "gui/ListWidget.h"
-#include "gui/ThemeEval.h"
+#include "gui/options.h"
#include "gui/saveload.h"
+#include "gui/ThemeEval.h"
#include "engines/dialogs.h"
#include "engines/engine.h"
@@ -49,16 +51,17 @@
using GUI::CommandSender;
using GUI::StaticTextWidget;
-enum {
- kSaveCmd = 'SAVE',
- kLoadCmd = 'LOAD',
- kPlayCmd = 'PLAY',
- kOptionsCmd = 'OPTN',
- kHelpCmd = 'HELP',
- kAboutCmd = 'ABOU',
- kQuitCmd = 'QUIT',
- kRTLCmd = 'RTL ',
- kChooseCmd = 'CHOS'
+class ConfigDialog : public GUI::OptionsDialog {
+protected:
+#ifdef SMALL_SCREEN_DEVICE
+ GUI::Dialog *_keysDialog;
+#endif
+
+public:
+ ConfigDialog(bool subtitleControls);
+ ~ConfigDialog();
+
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
};
MainMenuDialog::MainMenuDialog(Engine *engine)
@@ -83,31 +86,37 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
StaticTextWidget *version = new StaticTextWidget(this, "GlobalMenu.Version", gScummVMVersionDate);
version->setAlign(Graphics::kTextAlignCenter);
- new GUI::ButtonWidget(this, "GlobalMenu.Resume", "Resume", kPlayCmd, 'P');
+ new GUI::ButtonWidget(this, "GlobalMenu.Resume", _("~R~esume"), 0, kPlayCmd, 'P');
- _loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", "Load", kLoadCmd, 'L');
+ _loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", _("~L~oad"), 0, kLoadCmd);
// TODO: setEnabled -> setVisible
_loadButton->setEnabled(_engine->hasFeature(Engine::kSupportsLoadingDuringRuntime));
- _saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", "Save", kSaveCmd, 'S');
+ _saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", _("~S~ave"), 0, kSaveCmd);
// TODO: setEnabled -> setVisible
_saveButton->setEnabled(_engine->hasFeature(Engine::kSupportsSavingDuringRuntime));
- new GUI::ButtonWidget(this, "GlobalMenu.Options", "Options", kOptionsCmd, 'O');
+ new GUI::ButtonWidget(this, "GlobalMenu.Options", _("~O~ptions"), 0, kOptionsCmd);
- new GUI::ButtonWidget(this, "GlobalMenu.About", "About", kAboutCmd, 'A');
+ // The help button is disabled by default.
+ // To enable "Help", an engine needs to use a subclass of MainMenuDialog
+ // (at least for now, we might change how this works in the future).
+ _helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", _("~H~elp"), 0, kHelpCmd);
+ _helpButton->setEnabled(false);
- _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", "Return to Launcher", kRTLCmd, 'R');
+ new GUI::ButtonWidget(this, "GlobalMenu.About", _("~A~bout"), 0, kAboutCmd);
+
+ _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", _("~R~eturn to Launcher"), 0, kRTLCmd);
_rtlButton->setEnabled(_engine->hasFeature(Engine::kSupportsRTL));
- new GUI::ButtonWidget(this, "GlobalMenu.Quit", "Quit", kQuitCmd, 'Q');
+ new GUI::ButtonWidget(this, "GlobalMenu.Quit", _("~Q~uit"), 0, kQuitCmd);
_aboutDialog = new GUI::AboutDialog();
_optionsDialog = new ConfigDialog(_engine->hasFeature(Engine::kSupportsSubtitleOptions));
- _loadDialog = new GUI::SaveLoadChooser("Load game:", "Load");
+ _loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"));
_loadDialog->setSaveMode(false);
- _saveDialog = new GUI::SaveLoadChooser("Save game:", "Save");
+ _saveDialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"));
_saveDialog->setSaveMode(true);
}
@@ -135,6 +144,9 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
case kAboutCmd:
_aboutDialog->runModal();
break;
+ case kHelpCmd:
+ // Not handled here -- needs to be handled by a subclass (for now)
+ break;
case kRTLCmd: {
Common::Event eventRTL;
eventRTL.type = Common::EVENT_RTL;
@@ -263,13 +275,13 @@ enum {
// "" as value for the domain, and in fact provide a somewhat better user
// experience at the same time.
ConfigDialog::ConfigDialog(bool subtitleControls)
- : GUI::OptionsDialog("", "ScummConfig") {
+ : GUI::OptionsDialog("", "GlobalConfig") {
//
// Sound controllers
//
- addVolumeControls(this, "ScummConfig.");
+ addVolumeControls(this, "GlobalConfig.");
setVolumeSettingsState(true); // could disable controls by GUI options
//
@@ -278,7 +290,7 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
if (subtitleControls) {
// Global talkspeed range of 0-255
- addSubtitleControls(this, "ScummConfig.", 255);
+ addSubtitleControls(this, "GlobalConfig.", 255);
setSubtitleSettingsState(true); // could disable controls by GUI options
}
@@ -286,11 +298,11 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
// Add the buttons
//
- new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C');
+ new GUI::ButtonWidget(this, "GlobalConfig.Ok", _("~O~K"), 0, GUI::kOKCmd);
+ new GUI::ButtonWidget(this, "GlobalConfig.Cancel", _("~C~ancel"), 0, GUI::kCloseCmd);
#ifdef SMALL_SCREEN_DEVICE
- new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K');
+ new GUI::ButtonWidget(this, "GlobalConfig.Keys", _("~K~eys"), 0, kKeysCmd);
_keysDialog = NULL;
#endif
}
diff --git a/engines/dialogs.h b/engines/dialogs.h
index 6bee7c5fb1..6e5338b317 100644
--- a/engines/dialogs.h
+++ b/engines/dialogs.h
@@ -27,7 +27,6 @@
#include "common/str.h"
#include "gui/dialog.h"
-#include "gui/options.h"
class Engine;
@@ -39,6 +38,19 @@ namespace GUI {
class MainMenuDialog : public GUI::Dialog {
public:
+ enum {
+ kSaveCmd = 'SAVE',
+ kLoadCmd = 'LOAD',
+ kPlayCmd = 'PLAY',
+ kOptionsCmd = 'OPTN',
+ kHelpCmd = 'HELP',
+ kAboutCmd = 'ABOU',
+ kQuitCmd = 'QUIT',
+ kRTLCmd = 'RTL ',
+ kChooseCmd = 'CHOS'
+ };
+
+public:
MainMenuDialog(Engine *engine);
~MainMenuDialog();
@@ -51,29 +63,20 @@ protected:
void load();
protected:
- Engine *_engine;
+ Engine *_engine;
- GUI::GraphicsWidget *_logo;
- GUI::ButtonWidget *_rtlButton;
- GUI::ButtonWidget *_loadButton;
- GUI::ButtonWidget *_saveButton;
- GUI::Dialog *_aboutDialog;
- GUI::Dialog *_optionsDialog;
- GUI::SaveLoadChooser *_loadDialog;
- GUI::SaveLoadChooser *_saveDialog;
-};
+ GUI::GraphicsWidget *_logo;
-class ConfigDialog : public GUI::OptionsDialog {
-protected:
-#ifdef SMALL_SCREEN_DEVICE
- GUI::Dialog *_keysDialog;
-#endif
+ GUI::ButtonWidget *_rtlButton;
+ GUI::ButtonWidget *_loadButton;
+ GUI::ButtonWidget *_saveButton;
+ GUI::ButtonWidget *_helpButton;
-public:
- ConfigDialog(bool subtitleControls);
- ~ConfigDialog();
+ GUI::Dialog *_aboutDialog;
+ GUI::Dialog *_optionsDialog;
- virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+ GUI::SaveLoadChooser *_loadDialog;
+ GUI::SaveLoadChooser *_saveDialog;
};
#endif
diff --git a/engines/draci/animation.cpp b/engines/draci/animation.cpp
index e46582487c..2bedbe1801 100644
--- a/engines/draci/animation.cpp
+++ b/engines/draci/animation.cpp
@@ -166,10 +166,10 @@ void Animation::drawFrame(Surface *surface) {
const SoundSample *sample = _samples[_currentFrame];
if (_hasChangedFrame && sample) {
+ uint duration = _vm->_sound->playSound(sample, Audio::Mixer::kMaxChannelVolume, false);
debugC(3, kDraciSoundDebugLevel,
- "Playing sample on animation %d, frame %d: %d+%d at %dHz",
- _id, _currentFrame, sample->_offset, sample->_length, sample->_frequency);
- _vm->_sound->playSound(sample, Audio::Mixer::kMaxChannelVolume, false);
+ "Playing sample on animation %d, frame %d: %d+%d at %dHz: %dms",
+ _id, _currentFrame, sample->_offset, sample->_length, sample->_frequency, duration);
}
_hasChangedFrame = false;
}
diff --git a/engines/draci/barchive.cpp b/engines/draci/barchive.cpp
index 2ed2a9b591..8f9e836ba3 100644
--- a/engines/draci/barchive.cpp
+++ b/engines/draci/barchive.cpp
@@ -283,8 +283,7 @@ BAFile *BArchive::loadFileBAR(uint i) {
tmp ^= _files[i]._data[j];
}
- debugC(3, kDraciArchiverDebugLevel, "Cached file %d from archive %s",
- i, _path.c_str());
+ debugC(2, kDraciArchiverDebugLevel, "Read %d bytes", _files[i]._length);
assert(tmp == _files[i]._crc && "CRC checksum mismatch");
return _files + i;
@@ -385,7 +384,7 @@ const BAFile *BArchive::getFile(uint i) {
// Check if file has already been opened and return that
if (_files[i]._data) {
- debugC(2, kDraciArchiverDebugLevel, "Success");
+ debugC(2, kDraciArchiverDebugLevel, "Cached");
return _files + i;
}
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index c3204fc656..07a9928cfa 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -71,6 +71,16 @@ const ADGameDescription gameDescriptions[] = {
GUIO_NONE
},
+ {
+ "draci",
+ 0,
+ AD_ENTRY1s("INIT.DFW", "9a7115b91cdea361bcaff3e046ac7ded", 906),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+
AD_TABLE_END_MARKER
};
@@ -94,7 +104,11 @@ const ADParams detectionParams = {
// Flags
0,
// Global GUI options
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class DraciMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index cbf878279b..cd3920b30d 100644
--- a/engines/draci/draci.cpp
+++ b/engines/draci/draci.cpp
@@ -71,7 +71,7 @@ const char *dubbingPath = "CD.SAM";
const char *musicPathMask = "HUDBA%d.MID";
const uint kSoundsFrequency = 13000;
-const uint kDubbingFrequency = 22000;
+const uint kDubbingFrequency = 22050;
DraciEngine::DraciEngine(OSystem *syst, const ADGameDescription *gameDesc)
: Engine(syst) {
@@ -105,6 +105,39 @@ bool DraciEngine::hasFeature(EngineFeature f) const {
(f == kSupportsSavingDuringRuntime);
}
+static SoundArchive* openAnyPossibleDubbing() {
+ debugC(1, kDraciGeneralDebugLevel, "Trying to find original dubbing");
+ LegacySoundArchive *legacy = new LegacySoundArchive(dubbingPath, kDubbingFrequency);
+ if (legacy->isOpen() && legacy->size()) {
+ debugC(1, kDraciGeneralDebugLevel, "Found original dubbing");
+ return legacy;
+ }
+ delete legacy;
+
+ // The original uncompressed dubbing cannot be found. Try to open the
+ // newer compressed version.
+ debugC(1, kDraciGeneralDebugLevel, "Trying to find compressed dubbing");
+ ZipSoundArchive *zip = new ZipSoundArchive();
+
+ zip->openArchive("dub-raw.zzz", "buf", RAW80, kDubbingFrequency);
+ if (zip->isOpen() && zip->size()) return zip;
+#ifdef USE_FLAC
+ zip->openArchive("dub-flac.zzz", "flac", FLAC);
+ if (zip->isOpen() && zip->size()) return zip;
+#endif
+#ifdef USE_VORBIS
+ zip->openArchive("dub-ogg.zzz", "ogg", OGG);
+ if (zip->isOpen() && zip->size()) return zip;
+#endif
+#ifdef USE_MAD
+ zip->openArchive("dub-mp3.zzz", "mp3", MP3);
+ if (zip->isOpen() && zip->size()) return zip;
+#endif
+
+ // Return an empty (but initialized) archive anyway.
+ return zip;
+}
+
int DraciEngine::init() {
// Initialize graphics using following:
initGraphics(kScreenWidth, kScreenHeight, false);
@@ -123,15 +156,15 @@ int DraciEngine::init() {
_itemImagesArchive = new BArchive(itemImagesPath);
_stringsArchive = new BArchive(stringsPath);
- _soundsArchive = new SoundArchive(soundsPath, kSoundsFrequency);
- _dubbingArchive = new SoundArchive(dubbingPath, kDubbingFrequency);
+ _soundsArchive = new LegacySoundArchive(soundsPath, kSoundsFrequency);
+ _dubbingArchive = openAnyPossibleDubbing();
_sound = new Sound(_mixer);
- MidiDriverType 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::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ //bool adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
- _midiDriver = MidiDriver::createMidi(midiDriver);
+ _midiDriver = MidiDriver::createMidi(dev);
if (native_mt32)
_midiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
@@ -253,19 +286,20 @@ void DraciEngine::handleEvents() {
if (escRoom >= 0) {
// Schedule room change
- // TODO: gate 0 (always present) is not
- // always best for returning from the
- // map, e.g. in the starting location.
- // also, after loading the game, we
- // shouldn't run any gate program, but
- // rather restore the state of all
- // objects.
+ // TODO: gate 0 (always present) is not always best for
+ // returning from the map, e.g. in the starting location.
+ // also, after loading the game, we shouldn't run any gate
+ // program, but rather restore the state of all objects.
_game->scheduleEnteringRoomUsingGate(escRoom, 0);
- // Immediately cancel any running animation or dubbing.
+ // Immediately cancel any running animation or dubbing and
+ // end any currently running GPL programs. In the intro it
+ // works as intended---skipping the rest of it.
+ //
+ // In the map, this causes that animation on newly
+ // discovered locations will be re-run next time and
+ // cut-scenes won't be played.
_game->setExitLoop(true);
-
- // End any currently running GPL programs
_script->endCurrentProgram(true);
}
break;
@@ -301,6 +335,16 @@ void DraciEngine::handleEvents() {
openMainMenuDialog();
}
break;
+ case Common::KEYCODE_COMMA:
+ case Common::KEYCODE_PERIOD:
+ case Common::KEYCODE_SLASH:
+ if ((_game->getLoopStatus() == kStatusOrdinary ||
+ _game->getLoopStatus() == kStatusInventory) &&
+ _game->getLoopSubstatus() == kOuterLoop &&
+ _game->getRoomNum() != _game->getMapRoom()) {
+ _game->inventorySwitch(event.kbd.keycode);
+ }
+ break;
default:
break;
}
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index 0e4b3386ec..8f3ad12cfd 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "common/keyboard.h"
#include "common/serializer.h"
#include "common/stream.h"
#include "common/system.h"
@@ -174,8 +175,20 @@ void Game::start() {
// Call the outer loop doing all the hard job.
loop(kOuterLoop, false);
- }
+ // Fade out the palette after leaving the location.
+ fadePalette(true);
+
+ if (!isReloaded()) {
+ // We are changing location. Run the hero's LOOK
+ // program to trigger a possible cut-scene. This is
+ // the behavior of the original game player, whose
+ // intention was to run the cut sequences after the
+ // certain location change.
+ const GameObject *dragon = getObject(kDragonObject);
+ _vm->_script->run(dragon->_program, dragon->_look);
+ }
+ }
}
void Game::init() {
@@ -193,7 +206,8 @@ void Game::init() {
_animUnderCursor = NULL;
_currentItem = _itemUnderCursor = NULL;
-
+ _previousItemPosition = -1;
+
_vm->_mouse->setCursorType(kHighlightedCursor); // anything different from kNormalCursor
_objUnderCursor = NULL;
@@ -263,8 +277,8 @@ void Game::handleOrdinaryLoop(int x, int y) {
if (_vm->_mouse->lButtonPressed()) {
_vm->_mouse->lButtonSet(false);
- if (_currentItem) {
- putItem(_currentItem, 0);
+ if (getCurrentItem()) {
+ putItem(getCurrentItem(), getPreviousItemPosition());
updateOrdinaryCursor();
} else {
if (_objUnderCursor) {
@@ -318,6 +332,16 @@ void Game::handleOrdinaryLoop(int x, int y) {
}
}
+int Game::inventoryPositionFromMouse() const {
+ const int column = CLIP(scummvm_lround(
+ (_vm->_mouse->getPosX() - kInventoryX + kInventoryItemWidth / 2.) /
+ kInventoryItemWidth) - 1, 0L, (long) kInventoryColumns - 1);
+ const int line = CLIP(scummvm_lround(
+ (_vm->_mouse->getPosY() - kInventoryY + kInventoryItemHeight / 2.) /
+ kInventoryItemHeight) - 1, 0L, (long) kInventoryLines - 1);
+ return line * kInventoryColumns + column;
+}
+
void Game::handleInventoryLoop() {
if (_loopSubstatus != kOuterLoop) {
return;
@@ -344,19 +368,12 @@ void Game::handleInventoryLoop() {
// If there is an inventory item under the cursor and we aren't
// holding any item, run its look GPL program
- if (_itemUnderCursor && !_currentItem) {
+ if (_itemUnderCursor && !getCurrentItem()) {
_vm->_script->runWrapper(_itemUnderCursor->_program, _itemUnderCursor->_look, true, false);
// Otherwise, if we are holding an item, try to place it inside the
// inventory
- } else if (_currentItem) {
- const int column = CLIP(scummvm_lround(
- (_vm->_mouse->getPosX() - kInventoryX + kInventoryItemWidth / 2.) /
- kInventoryItemWidth) - 1, 0L, (long) kInventoryColumns - 1);
- const int line = CLIP(scummvm_lround(
- (_vm->_mouse->getPosY() - kInventoryY + kInventoryItemHeight / 2.) /
- kInventoryItemHeight) - 1, 0L, (long) kInventoryLines - 1);
- const int index = line * kInventoryColumns + column;
- putItem(_currentItem, index);
+ } else if (getCurrentItem()) {
+ putItem(getCurrentItem(), inventoryPositionFromMouse());
updateInventoryCursor();
}
} else if (_vm->_mouse->rButtonPressed()) {
@@ -372,8 +389,9 @@ void Game::handleInventoryLoop() {
// The first is that there is no item in our hands.
// In that case, just take the inventory item from the inventory.
- if (!_currentItem) {
- _currentItem = _itemUnderCursor;
+ if (!getCurrentItem()) {
+ setCurrentItem(_itemUnderCursor);
+ setPreviousItemPosition(inventoryPositionFromMouse());
removeItem(_itemUnderCursor);
// The second is that there *is* an item in our hands.
@@ -413,6 +431,22 @@ void Game::handleDialogueLoop() {
}
}
+void Game::fadePalette(bool fading_out) {
+ const byte *startPal = NULL;
+ const byte *endPal = _currentRoom._palette >= 0
+ ? _vm->_paletteArchive->getFile(_currentRoom._palette)->_data
+ : NULL;
+ if (fading_out) {
+ startPal = endPal;
+ endPal = NULL;
+ }
+ for (int i = 1; i <= kBlackFadingIterations; ++i) {
+ _vm->_system->delayMillis(kBlackFadingTimeUnit);
+ _vm->_screen->interpolatePalettes(startPal, endPal, 0, kNumColours, i, kBlackFadingIterations);
+ _vm->_screen->copyToScreen();
+ }
+}
+
void Game::advanceAnimationsAndTestLoopExit() {
// Fade the palette if requested
if (_fadePhase > 0 && (_vm->_system->getMillis() - _fadeTick) >= kFadingTimeUnit) {
@@ -470,7 +504,7 @@ void Game::advanceAnimationsAndTestLoopExit() {
// callbacks) and redraw screen
_vm->_anims->drawScene(_vm->_screen->getSurface());
_vm->_screen->copyToScreen();
- _vm->_system->delayMillis(20);
+ _vm->_system->delayMillis(kTimeUnit);
// If the hero has arrived at his destination, after even the last
// phase was correctly animated, run the callback.
@@ -614,10 +648,10 @@ void Game::updateOrdinaryCursor() {
// If there is no game object under the cursor, try using the room itself
if (!_objUnderCursor) {
if (_vm->_script->testExpression(_currentRoom._program, _currentRoom._canUse)) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kHighlightedCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, true);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), true);
}
mouseChanged = true;
}
@@ -628,10 +662,10 @@ void Game::updateOrdinaryCursor() {
// update the cursor image (highlight it).
if (_objUnderCursor->_walkDir == 0) {
if (_vm->_script->testExpression(_objUnderCursor->_program, _objUnderCursor->_canUse)) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kHighlightedCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, true);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), true);
}
mouseChanged = true;
}
@@ -645,10 +679,10 @@ void Game::updateOrdinaryCursor() {
// Load the appropriate cursor (item image if an item is held or ordinary cursor
// if not)
if (!mouseChanged) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kNormalCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, false);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), false);
}
}
}
@@ -659,19 +693,19 @@ void Game::updateInventoryCursor() {
if (_itemUnderCursor) {
if (_vm->_script->testExpression(_itemUnderCursor->_program, _itemUnderCursor->_canUse)) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kHighlightedCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, true);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), true);
}
mouseChanged = true;
}
}
if (!mouseChanged) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kNormalCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, false);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), false);
}
}
}
@@ -723,6 +757,8 @@ const GameObject *Game::getObjectWithAnimation(const Animation *anim) const {
}
void Game::removeItem(GameItem *item) {
+ if (!item)
+ return;
for (uint i = 0; i < kInventorySlots; ++i) {
if (_inventory[i] == item) {
_inventory[i] = NULL;
@@ -744,7 +780,7 @@ void Game::loadItemAnimation(GameItem *item) {
void Game::putItem(GameItem *item, int position) {
// Empty our hands
- _currentItem = NULL;
+ setCurrentItem(NULL);
if (!item)
return;
@@ -758,6 +794,7 @@ void Game::putItem(GameItem *item, int position) {
break;
}
}
+ setPreviousItemPosition(position);
const int line = position / kInventoryColumns + 1;
const int column = position % kInventoryColumns + 1;
@@ -845,6 +882,55 @@ void Game::inventoryReload() {
for (uint i = 0; i < kInventorySlots; ++i) {
putItem(_inventory[i], i);
}
+ setPreviousItemPosition(0);
+}
+
+void Game::inventorySwitch(int keycode) {
+ switch (keycode) {
+ case Common::KEYCODE_SLASH:
+ // Switch between holding an item and the ordinary mouse cursor.
+ if (!getCurrentItem()) {
+ if (getPreviousItemPosition() >= 0) {
+ GameItem* last_item = _inventory[getPreviousItemPosition()];
+ setCurrentItem(last_item);
+ removeItem(last_item);
+ }
+ } else {
+ putItem(getCurrentItem(), getPreviousItemPosition());
+ }
+ break;
+ case Common::KEYCODE_COMMA:
+ case Common::KEYCODE_PERIOD:
+ // Iterate between the items in the inventory.
+ if (getCurrentItem()) {
+ assert(getPreviousItemPosition() >= 0);
+ int direction = keycode == Common::KEYCODE_PERIOD ? +1 : -1;
+ // Find the next available item.
+ int pos = getPreviousItemPosition() + direction;
+ while (true) {
+ if (pos < 0)
+ pos += kInventorySlots;
+ else if (pos >= kInventorySlots)
+ pos -= kInventorySlots;
+ if (pos == getPreviousItemPosition() || _inventory[pos]) {
+ break;
+ }
+ pos += direction;
+ }
+ // Swap it with the current item.
+ putItem(getCurrentItem(), getPreviousItemPosition());
+ GameItem* new_item = _inventory[pos];
+ setCurrentItem(new_item);
+ setPreviousItemPosition(pos);
+ removeItem(new_item);
+ }
+ break;
+ }
+ if (getLoopStatus() == kStatusOrdinary) {
+ updateOrdinaryCursor();
+ } else {
+ updateInventoryCursor();
+ }
}
void Game::dialogueMenu(int dialogueID) {
@@ -1297,10 +1383,11 @@ void Game::enterNewRoom() {
loadRoomObjects();
loadOverlays();
- // Set room palette
- const BAFile *f;
- f = _vm->_paletteArchive->getFile(_currentRoom._palette);
- _vm->_screen->setPalette(f->_data, 0, kNumColours);
+ // Draw the scene with the black palette and slowly fade into the right palette.
+ _vm->_screen->setPalette(NULL, 0, kNumColours);
+ _vm->_anims->drawScene(_vm->_screen->getSurface());
+ _vm->_screen->copyToScreen();
+ fadePalette(false);
// Run the program for the gate the dragon came through
debugC(6, kDraciLogicDebugLevel, "Running program for gate %d", _newGate);
diff --git a/engines/draci/game.h b/engines/draci/game.h
index 3d02489da7..21baaed5cc 100644
--- a/engines/draci/game.h
+++ b/engines/draci/game.h
@@ -65,9 +65,16 @@ enum SpeechConstants {
kStandardSpeed = 60
};
-// One fading phase is 50ms.
enum FadeConstants {
- kFadingTimeUnit = 50
+ // One fading phase called from the game scripts is 50ms.
+ kFadingTimeUnit = 50,
+ // Fading in/out when entering/leaving a location takes 15 iterations of (at least) 7ms each.
+ kBlackFadingIterations = 15,
+ kBlackFadingTimeUnit = 7
+};
+
+enum AnimationConstants {
+ kTimeUnit = 20
};
/** Inventory related magical constants */
@@ -255,6 +262,8 @@ public:
GameItem *getItem(int id) { return id >= 0 && id < (int) _info._numItems ? &_items[id] : NULL; }
GameItem *getCurrentItem() const { return _currentItem; }
void setCurrentItem(GameItem *item) { _currentItem = item; }
+ int getPreviousItemPosition() const { return _previousItemPosition; }
+ void setPreviousItemPosition(int pos) { _previousItemPosition = pos; }
void removeItem(GameItem *item);
void loadItemAnimation(GameItem *item);
void putItem(GameItem *item, int position);
@@ -292,6 +301,7 @@ public:
void inventoryDraw();
void inventoryDone();
void inventoryReload();
+ void inventorySwitch(int keycode);
void dialogueMenu(int dialogueID);
int dialogueDraw();
@@ -325,11 +335,13 @@ public:
private:
void updateOrdinaryCursor();
void updateInventoryCursor();
+ int inventoryPositionFromMouse() const;
void handleOrdinaryLoop(int x, int y);
void handleInventoryLoop();
void handleDialogueLoop();
void updateTitle(int x, int y);
void updateCursor();
+ void fadePalette(bool fading_out);
void advanceAnimationsAndTestLoopExit();
void handleStatusChangeByMouse();
@@ -353,6 +365,10 @@ private:
GameItem *_currentItem;
GameItem *_itemUnderCursor;
+ // Last position in the inventory of the item currently in the hands, resp. of the item that
+ // was last in our hands.
+ int _previousItemPosition;
+
GameItem *_inventory[kInventorySlots];
Room _currentRoom;
diff --git a/engines/draci/module.mk b/engines/draci/module.mk
index a172e4b939..1f80737135 100644
--- a/engines/draci/module.mk
+++ b/engines/draci/module.mk
@@ -17,9 +17,6 @@ MODULE_OBJS := \
surface.o \
walking.o
-MODULE_DIRS += \
- engines/draci
-
# This module can be built as a plugin
ifeq ($(ENABLE_DRACI), DYNAMIC_PLUGIN)
PLUGIN := 1
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index a366740526..7a6a68618d 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -553,6 +553,7 @@ void Script::icoStat(const Common::Array<int> &params) {
// arrow leading outside a location), set it to standard.
if (_vm->_game->getCurrentItem() == item) {
_vm->_game->setCurrentItem(NULL);
+ _vm->_game->setPreviousItemPosition(-1);
if (_vm->_mouse->getCursorType() >= kItemCursor) {
_vm->_mouse->setCursorType(kNormalCursor);
}
@@ -561,6 +562,7 @@ void Script::icoStat(const Common::Array<int> &params) {
} else {
_vm->_game->loadItemAnimation(item);
_vm->_game->setCurrentItem(item);
+ _vm->_game->setPreviousItemPosition(0); // next time, try to place the item from the beginning
_vm->_mouse->loadItemCursor(item, false);
}
}
@@ -727,10 +729,10 @@ void Script::talk(const Common::Array<int> &params) {
// Speak the dubbing if possible
uint dubbingDuration = 0;
if (sample) {
- dubbingDuration = (uint) (1000.0 * sample->_length / sample->_frequency + 500.0);
+ dubbingDuration = _vm->_sound->playVoice(sample);
debugC(3, kDraciSoundDebugLevel, "Playing sentence %d: %d+%d with duration %dms",
sentenceID, sample->_offset, sample->_length, dubbingDuration);
- _vm->_sound->playVoice(sample);
+ dubbingDuration += 500;
}
// Record time
@@ -879,7 +881,7 @@ void Script::setPalette(const Common::Array<int> &params) {
}
// Immediately update the palette
_vm->_screen->copyToScreen();
- _vm->_system->delayMillis(20);
+ _vm->_system->delayMillis(kTimeUnit);
}
void Script::quitGame(const Common::Array<int> &params) {
diff --git a/engines/draci/sound.cpp b/engines/draci/sound.cpp
index 4fb2fd6309..c9244d7eac 100644
--- a/engines/draci/sound.cpp
+++ b/engines/draci/sound.cpp
@@ -23,11 +23,13 @@
*
*/
+#include "common/archive.h"
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/str.h"
#include "common/stream.h"
+#include "common/unzip.h"
#include "draci/sound.h"
#include "draci/draci.h"
@@ -36,21 +38,24 @@
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "sound/decoders/raw.h"
+#include "sound/decoders/mp3.h"
+#include "sound/decoders/vorbis.h"
+#include "sound/decoders/flac.h"
namespace Draci {
-void SoundArchive::openArchive(const Common::String &path) {
+void LegacySoundArchive::openArchive(const char *path) {
// Close previously opened archive (if any)
closeArchive();
- debugCN(2, kDraciArchiverDebugLevel, "Loading samples %s: ", path.c_str());
+ debugCN(1, kDraciArchiverDebugLevel, "Loading samples %s: ", path);
_f = new Common::File();
_f->open(path);
if (_f->isOpen()) {
- debugC(2, kDraciArchiverDebugLevel, "Success");
+ debugC(1, kDraciArchiverDebugLevel, "Success");
} else {
- debugC(2, kDraciArchiverDebugLevel, "Error");
+ debugC(1, kDraciArchiverDebugLevel, "Error");
delete _f;
_f = NULL;
return;
@@ -60,7 +65,7 @@ void SoundArchive::openArchive(const Common::String &path) {
_path = path;
// Read archive header
- debugC(2, kDraciArchiverDebugLevel, "Loading header");
+ debugC(1, kDraciArchiverDebugLevel, "Loading header");
uint totalLength = _f->readUint32LE();
const uint kMaxSamples = 4095; // The no-sound file is exactly 16K bytes long, so don't fail on short reads
@@ -76,26 +81,25 @@ void SoundArchive::openArchive(const Common::String &path) {
break;
}
if (_sampleCount > 0) {
- debugC(2, kDraciArchiverDebugLevel, "Archive info: %d samples, %d total length",
+ debugC(1, kDraciArchiverDebugLevel, "Archive info: %d samples, %d total length",
_sampleCount, totalLength);
_samples = new SoundSample[_sampleCount];
for (uint i = 0; i < _sampleCount; ++i) {
_samples[i]._offset = sampleStarts[i];
_samples[i]._length = sampleStarts[i+1] - sampleStarts[i];
_samples[i]._frequency = 0; // set in getSample()
- _samples[i]._data = NULL;
}
if (_samples[_sampleCount-1]._offset + _samples[_sampleCount-1]._length != totalLength &&
_samples[_sampleCount-1]._offset + _samples[_sampleCount-1]._length - _samples[0]._offset != totalLength) {
// WORKAROUND: the stored length is stored with the header for sounds and without the hader for dubbing. Crazy.
- debugC(2, kDraciArchiverDebugLevel, "Broken sound archive: %d != %d",
+ debugC(1, kDraciArchiverDebugLevel, "Broken sound archive: %d != %d",
_samples[_sampleCount-1]._offset + _samples[_sampleCount-1]._length,
totalLength);
closeArchive();
return;
}
} else {
- debugC(2, kDraciArchiverDebugLevel, "Archive info: empty");
+ debugC(1, kDraciArchiverDebugLevel, "Archive info: empty");
}
// Indicate that the archive has been successfully opened
@@ -103,12 +107,12 @@ void SoundArchive::openArchive(const Common::String &path) {
}
/**
- * @brief SoundArchive close method
+ * @brief LegacySoundArchive close method
*
* Closes the currently opened archive. It can be called explicitly to
* free up memory.
*/
-void SoundArchive::closeArchive() {
+void LegacySoundArchive::closeArchive() {
clearCache();
delete _f;
_f = NULL;
@@ -123,7 +127,7 @@ void SoundArchive::closeArchive() {
* Clears the cache of the open files inside the archive without closing it.
* If the files are subsequently accessed, they are read from the disk.
*/
-void SoundArchive::clearCache() {
+void LegacySoundArchive::clearCache() {
// Delete all cached data
for (uint i = 0; i < _sampleCount; ++i) {
_samples[i].close();
@@ -137,32 +141,121 @@ void SoundArchive::clearCache() {
*
* Loads individual samples from an archive to memory on demand.
*/
-SoundSample *SoundArchive::getSample(int i, uint freq) {
+SoundSample *LegacySoundArchive::getSample(int i, uint freq) {
// Check whether requested file exists
if (i < 0 || i >= (int) _sampleCount) {
return NULL;
}
debugCN(2, kDraciArchiverDebugLevel, "Accessing sample %d from archive %s... ",
- i, _path.c_str());
+ i, _path);
// Check if file has already been opened and return that
if (_samples[i]._data) {
- debugC(2, kDraciArchiverDebugLevel, "Success");
+ debugC(2, kDraciArchiverDebugLevel, "Cached");
} else {
+ // It would be nice to unify the approach with ZipSoundArchive
+ // and allocate a MemoryReadStream with buffer stored inside it
+ // that playSoundBuffer() would just play. Unfortunately,
+ // streams are not thread-safe and the same sample couldn't
+ // thus be played more than once at the same time (this holds
+ // even if we create a SeekableSubReadStream from it as this
+ // just uses the parent). The only thread-safe solution is to
+ // share a read-only buffer and allocate separate
+ // MemoryReadStream's on top of it.
+ _samples[i]._data = new byte[_samples[i]._length];
+ _samples[i]._format = RAW;
+
// Read in the file (without the file header)
_f->seek(_samples[i]._offset);
- _samples[i]._data = new byte[_samples[i]._length];
_f->read(_samples[i]._data, _samples[i]._length);
- debugC(3, kDraciArchiverDebugLevel, "Cached sample %d from archive %s",
- i, _path.c_str());
+ debugC(2, kDraciArchiverDebugLevel, "Read sample %d from archive %s",
+ i, _path);
}
_samples[i]._frequency = freq ? freq : _defaultFreq;
return _samples + i;
}
+void ZipSoundArchive::openArchive(const char *path, const char *extension, SoundFormat format, int raw_frequency) {
+ closeArchive();
+ if ((format == RAW || format == RAW80) && !raw_frequency) {
+ error("openArchive() expects frequency for RAW data");
+ return;
+ }
+
+ debugCN(1, kDraciArchiverDebugLevel, "Trying to open ZIP archive %s: ", path);
+ _archive = Common::makeZipArchive(path);
+ _path = path;
+ _extension = extension;
+ _format = format;
+ _defaultFreq = raw_frequency;
+
+ if (_archive) {
+ Common::ArchiveMemberList files;
+ _archive->listMembers(files);
+ _sampleCount = files.size();
+ debugC(1, kDraciArchiverDebugLevel, "Capacity %d", _sampleCount);
+ } else {
+ debugC(1, kDraciArchiverDebugLevel, "Failed");
+ }
+}
+
+void ZipSoundArchive::closeArchive() {
+ clearCache();
+ delete _archive;
+ _archive = NULL;
+ _path = _extension = NULL;
+ _sampleCount = _defaultFreq = 0;
+ _format = RAW;
+}
+
+void ZipSoundArchive::clearCache() {
+ // Just deallocate the link-list of (very short) headers for each
+ // dubbed sentence played in the current location. If the callers have
+ // not called .close() on any of the items, call them now.
+ for (Common::List<SoundSample>::iterator it = _cache.begin(); it != _cache.end(); ++it) {
+ it->close();
+ }
+ _cache.clear();
+}
+
+SoundSample *ZipSoundArchive::getSample(int i, uint freq) {
+ if (i < 0 || i >= (int) _sampleCount) {
+ return NULL;
+ }
+ debugCN(2, kDraciArchiverDebugLevel, "Accessing sample %d.%s from archive %s (format %d@%d, capacity %d): ",
+ i, _extension, _path, static_cast<int> (_format), _defaultFreq, _sampleCount);
+ if (freq != 0 && (_format != RAW && _format != RAW80)) {
+ error("Cannot resample a sound in compressed format");
+ return NULL;
+ }
+
+ // We cannot really cache anything, because createReadStreamForMember()
+ // returns the data as a ReadStream, which is not thread-safe. We thus
+ // read it again each time even if it has possibly been already cached
+ // a while ago. This is not such a problem for dubbing as for regular
+ // sound samples.
+ SoundSample sample;
+ sample._frequency = freq ? freq : _defaultFreq;
+ sample._format = _format;
+ // Read in the file (without the file header)
+ char file_name[20];
+ sprintf(file_name, "%d.%s", i+1, _extension);
+ sample._stream = _archive->createReadStreamForMember(file_name);
+ if (!sample._stream) {
+ debugC(2, kDraciArchiverDebugLevel, "Doesn't exist");
+ return NULL;
+ } else {
+ debugC(2, kDraciArchiverDebugLevel, "Read");
+ _cache.push_back(sample);
+ // Return a pointer that we own and which we will deallocate
+ // including its contents.
+ return &_cache.back();
+ }
+}
+
Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer), _muteSound(false), _muteVoice(false),
_showSubtitles(true), _talkSpeed(kStandardSpeed) {
@@ -192,28 +285,74 @@ SndHandle *Sound::getHandle() {
return NULL; // for compilers that don't support NORETURN
}
-void Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
+uint Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
sndHandleType handleType, bool loop) {
+ if (!buffer._stream && !buffer._data) {
+ warning("Empty stream");
+ return 0;
+ }
+ // Create a new SeekableReadStream which will be automatically disposed
+ // after the sample stops playing. Do not dispose the original
+ // data/stream though.
+ // Beware that if the sample comes from an archive (i.e., is stored in
+ // buffer._stream), then you must NOT play it more than once at the
+ // same time, because streams are not thread-safe. Playing it
+ // repeatedly is OK. Currently this is ensured by that archives are
+ // only used for dubbing, which is only played from one place in
+ // script.cpp, which blocks until the dubbed sentence has finished
+ // playing.
+ Common::SeekableReadStream* stream;
+ const int skip = buffer._format == RAW80 ? 80 : 0;
+ if (buffer._stream) {
+ stream = new Common::SeekableSubReadStream(
+ buffer._stream, skip, buffer._stream->size() /* end */, DisposeAfterUse::NO);
+ } else {
+ stream = new Common::MemoryReadStream(
+ buffer._data + skip, buffer._length - skip /* length */, DisposeAfterUse::NO);
+ }
- byte flags = Audio::FLAG_UNSIGNED;
+ Audio::SeekableAudioStream *reader = NULL;
+ switch (buffer._format) {
+ case RAW:
+ case RAW80:
+ reader = Audio::makeRawStream(stream, buffer._frequency, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+ break;
+#ifdef USE_MAD
+ case MP3:
+ reader = Audio::makeMP3Stream(stream, DisposeAfterUse::YES);
+ break;
+#endif
+#ifdef USE_VORBIS
+ case OGG:
+ reader = Audio::makeVorbisStream(stream, DisposeAfterUse::YES);
+ break;
+#endif
+#ifdef USE_FLAC
+ case FLAC:
+ reader = Audio::makeFLACStream(stream, DisposeAfterUse::YES);
+ break;
+#endif
+ default:
+ error("Unsupported compression format %d", static_cast<int> (buffer._format));
+ delete stream;
+ return 0;
+ }
+ const uint length = reader->getLength().msecs();
const Audio::Mixer::SoundType soundType = (handleType == kVoiceHandle) ?
Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType;
-
- // Don't use DisposeAfterUse::YES, because our caching system deletes samples by itself.
- Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
- Audio::makeRawStream(buffer._data, buffer._length, buffer._frequency, flags, DisposeAfterUse::NO),
- loop ? 0 : 1);
- _mixer->playStream(soundType, handle, stream, -1, volume);
+ Audio::AudioStream *audio_stream = Audio::makeLoopingAudioStream(reader, loop ? 0 : 1);
+ _mixer->playStream(soundType, handle, audio_stream, -1, volume);
+ return length;
}
-void Sound::playSound(const SoundSample *buffer, int volume, bool loop) {
+uint Sound::playSound(const SoundSample *buffer, int volume, bool loop) {
if (!buffer || _muteSound)
- return;
+ return 0;
SndHandle *handle = getHandle();
handle->type = kEffectHandle;
- playSoundBuffer(&handle->handle, *buffer, 2 * volume, handle->type, loop);
+ return playSoundBuffer(&handle->handle, *buffer, 2 * volume, handle->type, loop);
}
void Sound::pauseSound() {
@@ -237,13 +376,13 @@ void Sound::stopSound() {
}
}
-void Sound::playVoice(const SoundSample *buffer) {
+uint Sound::playVoice(const SoundSample *buffer) {
if (!buffer || _muteVoice)
- return;
+ return 0;
SndHandle *handle = getHandle();
handle->type = kVoiceHandle;
- playSoundBuffer(&handle->handle, *buffer, Audio::Mixer::kMaxChannelVolume, handle->type, false);
+ return playSoundBuffer(&handle->handle, *buffer, Audio::Mixer::kMaxChannelVolume, handle->type, false);
}
void Sound::pauseVoice() {
diff --git a/engines/draci/sound.h b/engines/draci/sound.h
index 28379b5429..6e9aae1b6e 100644
--- a/engines/draci/sound.h
+++ b/engines/draci/sound.h
@@ -28,50 +28,101 @@
#include "common/str.h"
#include "common/file.h"
+#include "common/list.h"
#include "sound/mixer.h"
+namespace Common {
+class Archive;
+class SeekableReadStream;
+}
+
namespace Draci {
+enum SoundFormat { RAW, RAW80, MP3, OGG, FLAC }; // RAW80 means skip the first 80 bytes
+
/**
* Represents individual files inside the archive.
*/
struct SoundSample {
- uint _offset;
+ uint _offset; // For internal use of LegacySoundArchive
uint _length;
- uint _frequency;
- byte* _data;
+ uint _frequency; // Only when _format == RAW or RAW80
+ SoundFormat _format;
+
+ byte *_data; // At most one of these two pointer can be non-NULL
+ Common::SeekableReadStream* _stream;
+
+ SoundSample() : _offset(0), _length(0), _frequency(0), _format(RAW), _data(NULL), _stream(NULL) { }
+ // The standard copy constructor is good enough, since we only store numbers and pointers.
+ // Don't call close() automaticall in the destructor, otherwise copying causes SIGSEGV.
void close() {
delete[] _data;
+ delete _stream;
_data = NULL;
+ _stream = NULL;
}
};
+/**
+ * An abstract wrapper around archives of sound samples or dubbing.
+ */
class SoundArchive {
public:
- SoundArchive(const Common::String &path, uint defaultFreq) :
- _path(), _samples(NULL), _sampleCount(0), _defaultFreq(defaultFreq), _opened(false), _f(NULL) {
- openArchive(path);
- }
+ SoundArchive() { }
+ virtual ~SoundArchive() { }
- ~SoundArchive() { closeArchive(); }
-
- void closeArchive();
- void openArchive(const Common::String &path);
- uint size() const { return _sampleCount; }
+ /**
+ * Returns the number of sound samples in the archive. Zero means that
+ * a fake empty archive has been opened and the caller may consider
+ * opening a different one, for example with compressed music.
+ */
+ virtual uint size() const = 0;
/**
* Checks whether there is an archive opened. Should be called before reading
- * from the archive to check whether openArchive() succeeded.
+ * from the archive to check whether opening of the archive has succeeded.
+ */
+ virtual bool isOpen() const = 0;
+
+ /**
+ * Removes cached samples from memory.
*/
- bool isOpen() const { return _opened; }
+ virtual void clearCache() = 0;
+
+ /**
+ * Caches a given sample into memory and returns a pointer into it. We
+ * own the returned pointer, but the user may call .close() on it,
+ * which deallocates the memory of the actual sample data. If freq is
+ * nonzero, then the sample is played at a different frequency (only
+ * works for uncompressed samples).
+ */
+ virtual SoundSample *getSample(int i, uint freq) = 0;
+};
- void clearCache();
+/**
+ * Reads CD.SAM (with dubbing) and CD2.SAM (with sound samples) from the
+ * original game. Caches all read samples in a thread-safe manner.
+ */
+class LegacySoundArchive : public SoundArchive {
+public:
+ LegacySoundArchive(const char *path, uint defaultFreq) :
+ _path(NULL), _samples(NULL), _sampleCount(0), _defaultFreq(defaultFreq), _opened(false), _f(NULL) {
+ openArchive(path);
+ }
+ virtual ~LegacySoundArchive() { closeArchive(); }
- SoundSample *getSample(int i, uint freq);
+ void openArchive(const char *path);
+ void closeArchive();
+
+ virtual uint size() const { return _sampleCount; }
+ virtual bool isOpen() const { return _opened; }
+
+ virtual void clearCache();
+ virtual SoundSample *getSample(int i, uint freq);
private:
- Common::String _path; ///< Path to file
+ const char *_path; ///< Path to file
SoundSample *_samples; ///< Internal array of files
uint _sampleCount; ///< Number of files in archive
uint _defaultFreq; ///< The default sampling frequency of the archived samples
@@ -79,6 +130,44 @@ private:
Common::File *_f; ///< Opened file
};
+/**
+ * Reads ZIP archives with uncompressed files containing lossy-compressed
+ * samples in arbitrary format. Doesn't do any real caching and is
+ * thread-safe.
+ */
+class ZipSoundArchive : public SoundArchive {
+public:
+ ZipSoundArchive() : _archive(NULL), _path(NULL), _extension(NULL), _format(RAW), _sampleCount(0), _defaultFreq(0), _cache() { }
+ virtual ~ZipSoundArchive() { closeArchive(); }
+
+ void openArchive(const char *path, const char *extension, SoundFormat format, int raw_frequency = 0);
+ void closeArchive();
+
+ virtual uint size() const { return _sampleCount; }
+ virtual bool isOpen() const { return _archive != NULL; }
+
+ virtual void clearCache();
+ virtual SoundSample *getSample(int i, uint freq);
+
+private:
+ Common::Archive *_archive;
+ const char *_path;
+ const char *_extension;
+ SoundFormat _format;
+ uint _sampleCount;
+ uint _defaultFreq;
+
+ // Since we typically play at most 1 dubbing at a time, we could get
+ // away with having just 1 record allocated and reusing it each time.
+ // However, that would be thread-unsafe if two samples were played.
+ // Although the player does not do that, it is nicer to allow for it in
+ // principle. For each dubbed sentence, we allocate a new record in
+ // the following link-list, which is cleared during each location
+ // change. The dubbed samples themselves are manually deallocated
+ // after they end.
+ Common::List<SoundSample> _cache;
+};
+
#define SOUND_HANDLES 10
enum sndHandleType {
@@ -100,13 +189,13 @@ public:
Sound(Audio::Mixer *mixer);
~Sound() {}
- void playSound(const SoundSample *buffer, int volume, bool loop);
+ uint playSound(const SoundSample *buffer, int volume, bool loop);
void pauseSound();
void resumeSound();
void stopSound();
bool isMutedSound() const { return _muteSound; }
- void playVoice(const SoundSample *buffer);
+ uint playVoice(const SoundSample *buffer);
void pauseVoice();
void resumeVoice();
void stopVoice();
@@ -120,7 +209,8 @@ public:
int talkSpeed() const { return _talkSpeed; }
private:
- void playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
+ // Returns the length of the sound sample in miliseconds.
+ uint playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
sndHandleType handleType, bool loop);
SndHandle *getHandle();
diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp
index e57972fbc5..02612832d2 100644
--- a/engines/draci/walking.cpp
+++ b/engines/draci/walking.cpp
@@ -324,7 +324,7 @@ void WalkingMap::drawOverlayRectangle(const Common::Point &p, byte colour, byte
}
int WalkingMap::pointsBetween(const Common::Point &p1, const Common::Point &p2) {
- return MAX(abs(p2.x - p1.x), abs(p2.y - p1.y));
+ return MAX(ABS(p2.x - p1.x), ABS(p2.y - p1.y));
}
Common::Point WalkingMap::interpolate(const Common::Point &p1, const Common::Point &p2, int i, int n) {
@@ -636,7 +636,7 @@ bool WalkingState::walkOnNextEdge() {
Movement WalkingState::animationForDirection(const Common::Point &here, const Common::Point &there) {
const int dx = there.x - here.x;
const int dy = there.y - here.y;
- if (abs(dx) >= abs(dy)) {
+ if (ABS(dx) >= ABS(dy)) {
return dx >= 0 ? kMoveRight : kMoveLeft;
} else {
return dy >= 0 ? kMoveDown : kMoveUp;
diff --git a/engines/drascula/actors.cpp b/engines/drascula/actors.cpp
index ff46d8201a..8523b5b158 100644
--- a/engines/drascula/actors.cpp
+++ b/engines/drascula/actors.cpp
@@ -77,6 +77,7 @@ void DrasculaEngine::hiccup(int counter) {
do {
counter--;
+ updateEvents();
updateRoom();
if (currentChapter == 3)
updateScreen(0, 0, 0, y, 320, 200, screenSurface);
@@ -99,6 +100,7 @@ void DrasculaEngine::hiccup(int counter) {
if (y == 0)
trackCharacter = 0;
}
+ pause(3);
} while (counter > 0);
updateRoom();
@@ -310,9 +312,9 @@ void DrasculaEngine::quadrant_2() {
float distanceX, distanceY;
if (currentChapter == 2)
- distanceX = abs(curX + curWidth - roomX);
+ distanceX = ABS(curX + curWidth - roomX);
else
- distanceX = abs(curX + curWidth / 2 - roomX);
+ distanceX = ABS(curX + curWidth / 2 - roomX);
distanceY = (curY + curHeight) - roomY;
@@ -352,9 +354,9 @@ void DrasculaEngine::quadrant_4() {
float distanceX, distanceY;
if (currentChapter == 2)
- distanceX = abs(curX + curWidth - roomX);
+ distanceX = ABS(curX + curWidth - roomX);
else
- distanceX = abs(curX + curWidth / 2 - roomX);
+ distanceX = ABS(curX + curWidth / 2 - roomX);
distanceY = roomY - (curY + curHeight);
@@ -449,6 +451,7 @@ void DrasculaEngine::placeVonBraun(int pointX) {
vonBraunHasMoved = 1;
for (;;) {
+ updateEvents();
updateRoom();
updateScreen();
if (trackVonBraun == 0) {
diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp
index 4a0b82d746..1d6f77b8eb 100644
--- a/engines/drascula/animation.cpp
+++ b/engines/drascula/animation.cpp
@@ -32,17 +32,23 @@ void DrasculaEngine::updateAnim(int y, int destX, int destY, int width, int heig
for (int n = 0; n < count; n++){
x++;
- copyBackground(x, y, destX, destY, width, height, src, screenSurface);
- if (copyRectangle)
+ if (copyRectangle) {
+ copyBackground(destX, destY, destX, destY, width, height, bgSurface, screenSurface);
copyRect(x, y, destX, destY, width, height, src, screenSurface);
+ } else {
+ copyBackground(x, y, destX, destY, width, height, src, screenSurface);
+ }
updateScreen(destX, destY, destX, destY, width, height, screenSurface);
x += width;
+ updateEvents();
pause(delayVal);
}
}
// This is the game's introduction sequence
void DrasculaEngine::animation_1_1() {
+ debug(4, "animation_1_1()");
+
int l, l2, p;
//int pixelPos[6];
@@ -141,9 +147,9 @@ void DrasculaEngine::animation_1_1() {
copyBackground(0, 0, 320 - l, 0, l, 200, drawSurface3, screenSurface);
copyBackground(l, 0, 0, 0, 320 - l, 200, bgSurface, screenSurface);
- copyRect(interf_x[l2], interf_y[l2], 156 - l, 45, 63, 31,
- drawSurface2, screenSurface);
+ copyRect(interf_x[l2], interf_y[l2], 156 - l, 45, 63, 31, drawSurface2, screenSurface);
updateScreen();
+ updateEvents();
p++;
if (p == 6) {
p = 0;
@@ -371,6 +377,8 @@ void DrasculaEngine::animation_1_1() {
// John falls in love with BJ, who is then abducted by Drascula
void DrasculaEngine::animation_2_1() {
+ debug(4, "animation_2_1()");
+
int l;
gotoObject(231, 91);
@@ -560,6 +568,8 @@ void DrasculaEngine::animation_2_1() {
// John Hacker talks with the bartender to book a room
void DrasculaEngine::animation_3_1() {
+ debug(4, "animation_3_1()");
+
loadPic("an11y13.alg", extraSurface);
playTalkSequence(3); // sequence 3, chapter 1
@@ -569,6 +579,8 @@ void DrasculaEngine::animation_3_1() {
// John Hacker talks with the pianist
void DrasculaEngine::animation_4_1() {
+ debug(4, "animation_4_1()");
+
loadPic("an12.alg", extraSurface);
talk(205);
@@ -605,6 +617,8 @@ void DrasculaEngine::animation_4_1() {
}
void DrasculaEngine::animation_2_2() {
+ debug(4, "animation_2_2()");
+
trackProtagonist = 0;
copyBackground();
moveCharacters();
@@ -640,6 +654,8 @@ void DrasculaEngine::animation_2_2() {
}
void DrasculaEngine::animation_4_2() {
+ debug(4, "animation_4_2()");
+
stopMusic();
flags[9] = 1;
@@ -704,6 +720,8 @@ void DrasculaEngine::animation_4_2() {
}
void DrasculaEngine::animation_14_2() {
+ debug(4, "animation_14_2()");
+
int cY = -160;
int l = 0;
@@ -735,6 +753,8 @@ void DrasculaEngine::animation_14_2() {
// The drunk tells us about Von Braun
void DrasculaEngine::animation_16_2() {
+ debug(4, "animation_16_2()");
+
char curPic[20];
talk_drunk(12);
talk(371);
@@ -812,6 +832,8 @@ asco:
}
void DrasculaEngine::animation_20_2() {
+ debug(4, "animation_20_2()");
+
talk_vonBraun(7, kVonBraunDoor);
talk_vonBraun(8, kVonBraunDoor);
talk(383);
@@ -842,6 +864,8 @@ void DrasculaEngine::animation_20_2() {
}
void DrasculaEngine::animation_23_2() {
+ debug(4, "animation_23_2()");
+
loadPic("an24.alg", frontSurface);
flags[21] = 1;
@@ -897,6 +921,8 @@ void DrasculaEngine::animation_23_2() {
}
void DrasculaEngine::animation_23_joined() {
+ debug(4, "animation_23_joined()");
+
int p_x = curX + 2, p_y = curY - 3;
int x[] = {1, 38, 75, 112, 75, 112, 75, 112, 149, 112, 149, 112, 149, 186, 223, 260,
1, 38, 75, 112, 149, 112, 149, 112, 149, 112, 149, 186, 223, 260, 260, 260, 260, 223};
@@ -910,6 +936,7 @@ void DrasculaEngine::animation_23_joined() {
copyRect(x[n], y[n], p_x, p_y, 36, 74, backSurface, screenSurface);
updateRefresh();
updateScreen(p_x, p_y, p_x, p_y, 36, 74, screenSurface);
+ updateEvents();
pause(5);
}
@@ -917,6 +944,8 @@ void DrasculaEngine::animation_23_joined() {
}
void DrasculaEngine::animation_23_joined2() {
+ debug(4, "animation_23_joined2()");
+
int p_x = curX + 4, p_y = curY;
int x[] = {1, 35, 69, 103, 137, 171, 205, 239, 273, 1, 35, 69, 103, 137};
int y[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 73, 73, 73, 73, 73};
@@ -930,6 +959,7 @@ void DrasculaEngine::animation_23_joined2() {
copyRect(x[n], y[n], p_x, p_y, 33, 71, backSurface, screenSurface);
updateRefresh();
updateScreen(p_x,p_y, p_x,p_y, 33,71, screenSurface);
+ updateEvents();
pause(5);
}
@@ -937,6 +967,8 @@ void DrasculaEngine::animation_23_joined2() {
}
void DrasculaEngine::animation_25_2() {
+ debug(4, "animation_25_2()");
+
int cY = 0;
loadPic("an14_2.alg", backSurface);
@@ -959,6 +991,7 @@ void DrasculaEngine::animation_25_2() {
updateRefresh();
updateScreen();
+ updateEvents();
}
finishSound();
@@ -967,6 +1000,8 @@ void DrasculaEngine::animation_25_2() {
}
void DrasculaEngine::animation_27_2() {
+ debug(4, "animation_27_2()");
+
flags[22] = 1;
selectVerb(kVerbNone);
@@ -986,6 +1021,8 @@ void DrasculaEngine::animation_27_2() {
}
void DrasculaEngine::animation_29_2() {
+ debug(4, "animation_29_2()");
+
if (flags[33] == 0) {
playTalkSequence(29); // sequence 29, chapter 2
} else
@@ -1002,6 +1039,8 @@ void DrasculaEngine::animation_29_2() {
}
void DrasculaEngine::animation_31_2() {
+ debug(4, "animation_31_2()");
+
talk_vonBraun(44, kVonBraunNormal);
placeVonBraun(-50);
pause(15);
@@ -1020,6 +1059,8 @@ void DrasculaEngine::animation_31_2() {
}
void DrasculaEngine::animation_35_2() {
+ debug(4, "animation_35_2()");
+
gotoObject(96, 165);
gotoObject(79, 165);
@@ -1049,7 +1090,10 @@ void DrasculaEngine::animation_35_2() {
fadeToBlack(2);
}
+// Use cross on Yoda
void DrasculaEngine::animation_2_3() {
+ debug(4, "animation_2_3()");
+
flags[0] = 1;
playMusic(13);
animation_3_3();
@@ -1070,6 +1114,8 @@ void DrasculaEngine::animation_2_3() {
}
void DrasculaEngine::animation_3_3() {
+ debug(4, "animation_3_3()");
+
int px = curX - 20, py = curY - 1;
loadPic("an2y_1.alg", frontSurface);
@@ -1085,6 +1131,8 @@ void DrasculaEngine::animation_3_3() {
}
void DrasculaEngine::animation_4_3() {
+ debug(4, "animation_4_3()");
+
int px = 120, py = 63;
loadPic("any_1.alg", frontSurface);
@@ -1100,6 +1148,8 @@ void DrasculaEngine::animation_4_3() {
}
void DrasculaEngine::animation_5_3() {
+ debug(4, "animation_5_3()");
+
int px = curX - 20, py = curY - 1;
loadPic("an3y_1.alg", frontSurface);
@@ -1115,6 +1165,8 @@ void DrasculaEngine::animation_5_3() {
}
void DrasculaEngine::animation_6_3() {
+ debug(4, "animation_6_3()");
+
int frame = 0, px = 112, py = 62;
int yoda_x[] = { 3 ,82, 161, 240, 3, 82 };
int yoda_y[] = { 3, 3, 3, 3, 94, 94 };
@@ -1133,6 +1185,7 @@ void DrasculaEngine::animation_6_3() {
copyBackground();
copyRect(yoda_x[frame], yoda_y[frame], px, py, 78, 90, frontSurface, screenSurface);
updateScreen(px, py, px, py, 78, 90, screenSurface);
+ updateEvents();
}
flags[2] = 1;
@@ -1144,6 +1197,8 @@ void DrasculaEngine::animation_6_3() {
}
void DrasculaEngine::animation_ray() {
+ debug(4, "animation_ray()");
+
loadPic("anr_1.alg", frontSurface, HALF_PAL);
loadPic("anr_2.alg", extraSurface);
loadPic("anr_3.alg", backSurface);
@@ -1171,6 +1226,8 @@ void DrasculaEngine::animation_ray() {
}
void DrasculaEngine::animation_7_4() {
+ debug(4, "animation_7_4()");
+
black();
talk(427);
fadeFromBlack(1);
@@ -1184,6 +1241,8 @@ void DrasculaEngine::animation_7_4() {
}
void DrasculaEngine::animation_1_5() {
+ debug(4, "animation_1_5()");
+
if (flags[0] == 0) {
talk(430);
talk_bj(16);
@@ -1212,6 +1271,7 @@ void DrasculaEngine::animation_1_5() {
break;
updateRoom();
updateScreen();
+ updateEvents();
}
trackProtagonist = 1;
@@ -1224,6 +1284,8 @@ void DrasculaEngine::animation_1_5() {
}
void DrasculaEngine::animation_5_5(){
+ debug(4, "animation_5_5(");
+
int h;
int frame = 0;
int boneX[] = {1, 99, 197, 1, 99, 197, 1, 99, 197};
@@ -1247,6 +1309,7 @@ void DrasculaEngine::animation_5_5(){
copyBackground();
copyRect(boneX[frame], boneY[frame], pixelX, pixelY, 97, 64, backSurface, screenSurface);
updateScreen(pixelX, pixelY, pixelX,pixelY, 97,64, screenSurface);
+ updateEvents();
}
copyBackground(52, 161, 198, 81, 26, 24, drawSurface3, screenSurface);
@@ -1257,6 +1320,7 @@ void DrasculaEngine::animation_5_5(){
copyBackground();
copyRect(boneX[frame], boneY[frame], pixelX, pixelY, 97, 64, frontSurface, screenSurface);
updateScreen(pixelX, pixelY, pixelX,pixelY, 97, 64, screenSurface);
+ updateEvents();
}
flags[6] = 1;
@@ -1279,11 +1343,13 @@ void DrasculaEngine::animation_5_5(){
pause(3);
copyBackground(flyX[frame], 1, 174, 79, 61, 109, backSurface, screenSurface);
updateScreen(174, 79, 174, 79, 61, 109, screenSurface);
+ updateEvents();
}
for (frame = 0; frame < 5; frame++) {
pause(3);
copyBackground(flyX[frame], 1, 174, 79, 61, 109, extraSurface, screenSurface);
updateScreen(174, 79, 174, 79, 61, 109, screenSurface);
+ updateEvents();
}
updateScreen(0, 0, 0, 0, 320, 200, bgSurface);
@@ -1299,6 +1365,8 @@ void DrasculaEngine::animation_5_5(){
}
void DrasculaEngine::animation_11_5() {
+ debug(4, "animation_11_5()");
+
flags[9] = 1;
if (flags[2] == 1 && flags[3] == 1 && flags[4] == 1)
animation_12_5();
@@ -1309,6 +1377,8 @@ void DrasculaEngine::animation_11_5() {
}
void DrasculaEngine::animation_12_5() {
+ debug(4, "animation_12_5()");
+
DacPalette256 bgPalette1;
DacPalette256 bgPalette2;
DacPalette256 bgPalette3;
@@ -1367,6 +1437,7 @@ void DrasculaEngine::animation_12_5() {
copyRect(rayX[frame], 1, 41, 0, 44, 44, backSurface, screenSurface);
copyRect(frusky_x[frame], 113, 205, 50, 38, 86, drawSurface3, screenSurface);
updateScreen();
+ updateEvents();
}
stopSound();
@@ -1383,6 +1454,7 @@ void DrasculaEngine::animation_12_5() {
updateRoom();
copyRect(elfrusky_x[frame], 47, 192, 39, 66, 106, backSurface, screenSurface);
updateScreen();
+ updateEvents();
}
animate("frel.bin", 16);
@@ -1415,6 +1487,8 @@ void DrasculaEngine::animation_12_5() {
}
void DrasculaEngine::animation_13_5() {
+ debug(4, "animation_13_5()");
+
int frank_x = 199;
int frame = 0;
int frus_x[] = {1, 46, 91, 136, 181, 226, 271};
@@ -1441,11 +1515,14 @@ void DrasculaEngine::animation_13_5() {
frame = 0;
trackProtagonist = 3;
}
+ updateEvents();
pause(6);
}
}
void DrasculaEngine::animation_14_5() {
+ debug(4, "animation_14_5()");
+
flags[11] = 1;
playSound(3);
updateRoom();
@@ -1469,6 +1546,8 @@ void DrasculaEngine::animation_14_5() {
}
void DrasculaEngine::animation_1_6() {
+ debug(4, "animation_1_6()");
+
trackProtagonist = 0;
curX = 103;
curY = 108;
@@ -1497,11 +1576,15 @@ void DrasculaEngine::animation_1_6() {
talk_drascula(28, 1);
talk(255);
talk_drascula(29, 1);
+ updateEvents();
fadeToBlack(1);
+ updateEvents();
clearRoom();
loadPic("time1.alg", screenSurface);
updateScreen();
+ updateEvents();
delay(930);
+ updateEvents();
clearRoom();
black();
hare_se_ve = 0;
@@ -1513,10 +1596,13 @@ void DrasculaEngine::animation_1_6() {
talk_drascula(30, 1);
talk(257);
fadeToBlack(0);
+ updateEvents();
clearRoom();
loadPic("time1.alg", screenSurface);
updateScreen();
+ updateEvents();
delay(900);
+ updateEvents();
clearRoom();
black();
updateRoom();
@@ -1540,6 +1626,8 @@ void DrasculaEngine::animation_1_6() {
}
void DrasculaEngine::animation_5_6() {
+ debug(4, "animation_5_6()");
+
int pY = -125;
animate("man.bin", 14);
@@ -1553,6 +1641,7 @@ void DrasculaEngine::animation_5_6() {
updateRefresh();
updateScreen();
+ updateEvents();
pause(2);
}
@@ -1560,6 +1649,8 @@ void DrasculaEngine::animation_5_6() {
}
void DrasculaEngine::animation_6_6() {
+ debug(4, "animation_6_6()");
+
animate("rct.bin", 11);
clearRoom();
selectVerb(kVerbNone);
@@ -1584,6 +1675,8 @@ void DrasculaEngine::animation_6_6() {
}
void DrasculaEngine::animation_9_6() {
+ debug(4, "animation_9_6()");
+
int v_cd;
animate("fin.bin", 14);
@@ -1660,6 +1753,8 @@ void DrasculaEngine::animation_9_6() {
}
void DrasculaEngine::animation_19_6() {
+ debug(4, "animation_19_6()");
+
copyBackground();
copyBackground(140, 23, 161, 69, 35, 80, drawSurface3, screenSurface);
@@ -1675,6 +1770,8 @@ void DrasculaEngine::animation_19_6() {
}
void DrasculaEngine::animation_12_2() {
+ debug(4, "animation_12_2()");
+
loadPic("an12.alg", extraSurface);
talk(356);
@@ -1705,6 +1802,8 @@ void DrasculaEngine::animation_12_2() {
}
void DrasculaEngine::animation_26_2() {
+ debug(4, "animation_26_2()");
+
loadPic("an12.alg", extraSurface);
talk(392);
@@ -1745,6 +1844,7 @@ void DrasculaEngine::animation_26_2() {
x = x + 50;
if (n == 2)
playSound(9);
+ updateEvents();
pause(3);
}
@@ -1762,6 +1862,8 @@ void DrasculaEngine::animation_26_2() {
}
void DrasculaEngine::animation_11_2() {
+ debug(4, "animation_11_2()");
+
loadPic("an11y13.alg", extraSurface);
playTalkSequence(11); // sequence 11, chapter 2
@@ -1770,6 +1872,8 @@ void DrasculaEngine::animation_11_2() {
}
void DrasculaEngine::animation_13_2() {
+ debug(4, "animation_13_2()");
+
loadPic("an11y13.alg", frontSurface);
if (flags[41] == 0) {
@@ -1780,6 +1884,8 @@ void DrasculaEngine::animation_13_2() {
}
void DrasculaEngine::animation_24_2() {
+ debug(4, "animation_24_2()");
+
if (curX < 178)
gotoObject(208, 136);
trackProtagonist = 3;
@@ -1810,6 +1916,8 @@ void DrasculaEngine::animation_24_2() {
}
void DrasculaEngine::animation_32_2() {
+ debug(4, "animation_32_2()");
+
loadPic("an32_1.alg", drawSurface3);
loadPic("an32_2.alg", backSurface);
@@ -1825,12 +1933,16 @@ void DrasculaEngine::animation_32_2() {
x = x + 65;
if (n < 2)
pause(4);
+
+ updateEvents();
}
loadPic("aux18.alg", drawSurface3);
}
void DrasculaEngine::animation_34_2() {
+ debug(4, "animation_34_2()");
+
trackProtagonist = 1;
updateRoom();
updateScreen();
@@ -1858,6 +1970,8 @@ void DrasculaEngine::animation_34_2() {
}
void DrasculaEngine::animation_36_2() {
+ debug(4, "animation_36_2()");
+
loadPic("an11y13.alg", extraSurface);
talk(404);
@@ -1871,7 +1985,10 @@ void DrasculaEngine::animation_36_2() {
loadPic(974, extraSurface);
}
+// Use sickle on plant
void DrasculaEngine::animation_7_2() {
+ debug(4, "animation_7_2()");
+
loadPic("an7_1.alg", backSurface);
loadPic("an7_2.alg", extraSurface);
loadPic("an7_3.alg", frontSurface);
@@ -1928,6 +2045,8 @@ void DrasculaEngine::animation_7_2() {
}
void DrasculaEngine::animation_5_2() {
+ debug(4, "animation_5_2()");
+
trackProtagonist = 0;
updateRoom();
updateScreen();
@@ -1964,6 +2083,8 @@ void DrasculaEngine::animation_5_2() {
}
void DrasculaEngine::animation_6_2() {
+ debug(4, "animation_6_2()");
+
stopMusic();
flags[9] = 1;
@@ -2005,6 +2126,8 @@ void DrasculaEngine::animation_6_2() {
}
void DrasculaEngine::animation_33_2() {
+ debug(4, "animation_33_2()");
+
stopMusic();
flags[9] = 1;
@@ -2052,6 +2175,8 @@ void DrasculaEngine::animation_33_2() {
}
void DrasculaEngine::animation_1_4() {
+ debug(4, "animation_1_4()");
+
if (flags[21] == 0) {
strcpy(objName[2], "igor");
talk(275);
@@ -2108,6 +2233,8 @@ void DrasculaEngine::animation_1_4() {
}
void DrasculaEngine::animation_5_4(){
+ debug(4, "animation_5_4(");
+
trackProtagonist = 3;
loadPic("anh_dr.alg", backSurface);
gotoObject(99, 160);
@@ -2136,6 +2263,8 @@ void DrasculaEngine::animation_5_4(){
}
void DrasculaEngine::animation_6_4() {
+ debug(4, "animation_6_4()");
+
int prevRoom = roomNumber;
roomNumber = 26;
@@ -2161,6 +2290,8 @@ void DrasculaEngine::animation_6_4() {
}
void DrasculaEngine::animation_8_4() {
+ debug(4, "animation_8_4()");
+
int bookcaseX[] = {1, 75, 149, 223, 1, 75, 149, 223, 149, 223, 149, 223, 149, 223};
int bookcaseY[] = {1, 1, 1, 1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74};
@@ -2170,6 +2301,7 @@ void DrasculaEngine::animation_8_4() {
pause(2);
copyBackground(bookcaseX[frame], bookcaseY[frame], 77, 45, 73, 72, frontSurface, screenSurface);
updateScreen(77, 45, 77, 45, 73, 72, screenSurface);
+ updateEvents();
}
loadPic(96, frontSurface);
@@ -2177,6 +2309,8 @@ void DrasculaEngine::animation_8_4() {
}
void DrasculaEngine::activatePendulum() {
+ debug(4, "activatePendulum()");
+
flags[1] = 2;
hare_se_ve = 0;
roomNumber = 102;
diff --git a/engines/mohawk/myst_pict.h b/engines/drascula/console.cpp
index 0684e3352a..d017468285 100644
--- a/engines/mohawk/myst_pict.h
+++ b/engines/drascula/console.cpp
@@ -23,35 +23,41 @@
*
*/
-#ifndef MYST_PICT_H
-#define MYST_PICT_H
+#include "drascula/console.h"
+#include "gui/debugger.h"
+#include "drascula/drascula.h"
-#include "common/rect.h"
-#include "common/scummsys.h"
-#include "common/stream.h"
-#include "graphics/pixelformat.h"
-#include "graphics/surface.h"
+namespace Drascula {
-#include "mohawk/jpeg.h"
+Console::Console(DrasculaEngine *vm) : GUI::Debugger(), _vm(vm) {
+ DCmd_Register("room", WRAP_METHOD(Console, Cmd_Room));
+}
-namespace Mohawk {
+Console::~Console() {
+}
-class MystPICT {
-public:
- MystPICT(JPEGDecoder *jpegDecoder);
- ~MystPICT() {}
- Graphics::Surface *decodeImage(Common::SeekableReadStream *stream);
+void Console::preEnter() {
+}
-private:
- JPEGDecoder *_jpegDecoder;
- Common::Rect _imageRect;
- Graphics::PixelFormat _pixelFormat;
+void Console::postEnter() {
+}
- void decodeDirectBitsRect(Common::SeekableReadStream *stream, Graphics::Surface *image);
- void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel);
- void decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image);
-};
+bool Console::Cmd_Room(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Usage: changeCard <card>\n");
+ return true;
+ }
-} // End of namespace Mohawk
+ int roomNum = atoi(argv[1]);
-#endif
+ _vm->loadedDifferentChapter = 0;
+ _vm->enterRoom(roomNum);
+ _vm->selectVerb(kVerbNone);
+ _vm->clearRoom();
+ _vm->loadPic(roomNum, _vm->bgSurface, HALF_PAL);
+ _vm->selectionMade = 0;
+
+ return false;
+}
+
+} // End of namespace Drascula
diff --git a/engines/mohawk/jpeg.h b/engines/drascula/console.h
index ec87b1e7af..33e2f626e4 100644
--- a/engines/mohawk/jpeg.h
+++ b/engines/drascula/console.h
@@ -23,37 +23,29 @@
*
*/
-#ifndef MOHAWK_JPEG_H
-#define MOHAWK_JPEG_H
+#ifndef DRASCULA_CONSOLE_H
+#define DRASCULA_CONSOLE_H
-#include "common/scummsys.h"
-#include "common/stream.h"
+#include "gui/debugger.h"
-#include "graphics/video/codecs/codec.h"
-#include "graphics/jpeg.h"
-#include "graphics/pixelformat.h"
+namespace Drascula {
-namespace Mohawk {
+class DrasculaEngine;
-// Mohawk's JPEG Decoder
-// Basically a wrapper around JPEG which converts to RGB and also functions
-// as a Codec.
-
-class JPEGDecoder : public Graphics::Codec {
+class Console : public GUI::Debugger {
public:
- JPEGDecoder(bool freeSurfaceAfterUse);
- ~JPEGDecoder();
+ Console(DrasculaEngine *vm);
+ virtual ~Console(void);
- Graphics::Surface *decodeImage(Common::SeekableReadStream *stream);
- Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
private:
- Graphics::PixelFormat _pixelFormat;
- Graphics::JPEG *_jpeg;
- Graphics::Surface *_surface;
- bool _freeSurfaceAfterUse;
-};
+ DrasculaEngine *_vm;
-} // End of namespace Mohawk
+ bool Cmd_Room(int argc, const char **argv);
+};
+} // End of namespace Drascula
#endif
diff --git a/engines/drascula/converse.cpp b/engines/drascula/converse.cpp
index b2a7e217e6..0e70348148 100644
--- a/engines/drascula/converse.cpp
+++ b/engines/drascula/converse.cpp
@@ -131,6 +131,8 @@ void DrasculaEngine::cleanupString(char *string) {
}
void DrasculaEngine::converse(int index) {
+ debug(4, "converse(%d)", index);
+
char fileName[20];
sprintf(fileName, "op_%d.cal", index);
Common::SeekableReadStream *stream = _archives.open(fileName);
@@ -279,9 +281,15 @@ void DrasculaEngine::converse(int index) {
}
void DrasculaEngine::response(int function) {
- playTalkSequence(function);
+ debug(4, "response(%d)", function);
+
+ if (function != 31)
+ playTalkSequence(function);
if (currentChapter == 2) {
+ if (function == 16 || function == 20 || function == 23 || function == 29 || function == 31)
+ loadPic(menuBackground, backSurface);
+
if (function == 16)
animation_16_2();
else if (function == 20)
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index 76d48b7b89..c10222cadd 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -264,7 +264,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOMIDI
+ Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class DrasculaMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index 276554a24c..b1f16fa505 100644
--- a/engines/drascula/drascula.cpp
+++ b/engines/drascula/drascula.cpp
@@ -38,6 +38,7 @@
#include "sound/mixer.h"
#include "drascula/drascula.h"
+#include "drascula/console.h"
namespace Drascula {
@@ -114,6 +115,8 @@ DrasculaEngine::~DrasculaEngine() {
freeRoomsTable();
+ delete _console;
+
free(_charMap);
free(_itemLocations);
free(_polX);
@@ -172,6 +175,8 @@ Common::Error DrasculaEngine::run() {
_lang = kEnglish;
}
+ _console = new Console(this);
+
if (!loadDrasculaDat())
return Common::kUnknownError;
@@ -240,6 +245,8 @@ Common::Error DrasculaEngine::run() {
if (currentChapter != 3)
loadPic(96, frontSurface, COMPLETE_PAL);
+ loadPic(99, cursorSurface);
+
if (currentChapter == 1) {
} else if (currentChapter == 2) {
loadPic("pts.alg", drawSurface2);
@@ -271,6 +278,7 @@ Common::Error DrasculaEngine::run() {
loadPic(974, tableSurface);
if (currentChapter != 2) {
+ loadPic(99, cursorSurface);
loadPic(99, backSurface);
loadPic(97, extraSurface);
}
@@ -498,11 +506,15 @@ bool DrasculaEngine::runCurrentChapter() {
#else
if (rightMouseButton == 1 && _menuScreen) {
#endif
+ rightMouseButton = 0;
delay(100);
- if (currentChapter == 2)
+ if (currentChapter == 2) {
+ loadPic(menuBackground, cursorSurface);
loadPic(menuBackground, backSurface);
- else
+ } else {
+ loadPic(99, cursorSurface);
loadPic(99, backSurface);
+ }
setPalette((byte *)&gamePalette);
_menuScreen = false;
#ifndef _WIN32_WCE
@@ -523,18 +535,24 @@ bool DrasculaEngine::runCurrentChapter() {
if (rightMouseButton == 1 && !_menuScreen &&
!(currentChapter == 5 && pickedObject == 16)) {
#endif
+ rightMouseButton = 0;
delay(100);
characterMoved = 0;
if (trackProtagonist == 2)
trackProtagonist = 1;
- if (currentChapter == 4)
+ if (currentChapter == 4) {
loadPic("icons2.alg", backSurface);
- else if (currentChapter == 5)
+ loadPic("icons2.alg", cursorSurface);
+ } else if (currentChapter == 5) {
loadPic("icons3.alg", backSurface);
- else if (currentChapter == 6)
+ loadPic("icons3.alg", cursorSurface);
+ } else if (currentChapter == 6) {
loadPic("iconsp.alg", backSurface);
- else
+ loadPic("iconsp.alg", cursorSurface);
+ } else {
loadPic("icons.alg", backSurface);
+ loadPic("icons.alg", cursorSurface);
+ }
_menuScreen = true;
#ifndef _WIN32_WCE
updateEvents();
@@ -593,6 +611,9 @@ bool DrasculaEngine::runCurrentChapter() {
} else if (key == Common::KEYCODE_ESCAPE) {
if (!confirmExit())
return false;
+ } else if (key == Common::KEYCODE_TILDE || key == Common::KEYCODE_BACKQUOTE) {
+ _console->attach();
+ _console->onFrame();
} else if (currentChapter == 6 && key == Common::KEYCODE_0 && roomNumber == 61) {
loadPic("alcbar.alg", bgSurface, 255);
}
@@ -740,10 +761,10 @@ void DrasculaEngine::updateEvents() {
leftMouseButton = 0;
break;
case Common::EVENT_RBUTTONDOWN:
- rightMouseButton = 1;
+ // We changed semantic and react only on button up event
break;
case Common::EVENT_RBUTTONUP:
- rightMouseButton = 0;
+ rightMouseButton = 1;
break;
case Common::EVENT_QUIT:
// TODO
@@ -757,11 +778,17 @@ void DrasculaEngine::updateEvents() {
}
void DrasculaEngine::delay(int ms) {
- _system->delayMillis(ms * 2); // originally was 1
+ uint32 end = _system->getMillis() + ms * 2; // originally was 1
+
+ do {
+ _system->delayMillis(10);
+ updateEvents();
+ _system->updateScreen();
+ } while (_system->getMillis() < end);
}
void DrasculaEngine::pause(int duration) {
- _system->delayMillis(duration * 30); // was originally 2
+ delay(duration * 15);
}
int DrasculaEngine::getTime() {
diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h
index 4876cf3390..0a8b7c8c9b 100644
--- a/engines/drascula/drascula.h
+++ b/engines/drascula/drascula.h
@@ -317,6 +317,8 @@ static const int interf_y[] = { 51, 51, 51, 51, 83, 83, 83 };
struct RoomHandlers;
+class Console;
+
class DrasculaEngine : public ::Engine {
protected:
// Engine APIs
@@ -389,6 +391,7 @@ public:
// Graphics buffers/pointers
byte *bgSurface;
byte *backSurface;
+ byte *cursorSurface;
byte *drawSurface3;
byte *drawSurface2;
byte *tableSurface;
@@ -733,6 +736,8 @@ public:
private:
int _lang;
+ Console *_console;
+
CharInfo *_charMap;
int _charMapSize;
diff --git a/engines/drascula/graphics.cpp b/engines/drascula/graphics.cpp
index 00399816da..70085b99af 100644
--- a/engines/drascula/graphics.cpp
+++ b/engines/drascula/graphics.cpp
@@ -54,6 +54,7 @@ void DrasculaEngine::allocMemory() {
assert(crosshairCursor);
mouseCursor = (byte *)malloc(OBJWIDTH * OBJHEIGHT);
assert(mouseCursor);
+ cursorSurface = (byte *)malloc(64000);
}
void DrasculaEngine::freeMemory() {
@@ -67,6 +68,7 @@ void DrasculaEngine::freeMemory() {
free(frontSurface);
free(crosshairCursor);
free(mouseCursor);
+ free(cursorSurface);
}
void DrasculaEngine::moveCursor() {
@@ -90,6 +92,8 @@ void DrasculaEngine::moveCursor() {
}
void DrasculaEngine::loadPic(const char *NamePcc, byte *targetSurface, int colorCount) {
+ debug(5, "loadPic(%s)", NamePcc);
+
uint dataSize = 0;
byte *pcxData;
@@ -147,8 +151,7 @@ void DrasculaEngine::showFrame(Common::SeekableReadStream *stream, bool firstFra
free(prevFrame);
}
-void DrasculaEngine::copyBackground(int xorg, int yorg, int xdes, int ydes, int width,
- int height, byte *src, byte *dest) {
+void DrasculaEngine::copyBackground(int xorg, int yorg, int xdes, int ydes, int width, int height, byte *src, byte *dest) {
dest += xdes + ydes * 320;
src += xorg + yorg * 320;
/* Unoptimized code
@@ -190,16 +193,20 @@ void DrasculaEngine::copyRect(int xorg, int yorg, int xdes, int ydes, int width,
dest += xdes + ydes * 320;
src += xorg + yorg * 320;
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++)
- if (src[x + y * 320] != 255)
- dest[x + y * 320] = src[x + y * 320];
+ int ptr = 0;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ if (src[ptr] != 255)
+ dest[ptr] = src[ptr];
+ ptr++;
+ }
+ ptr += 320 - width;
+ }
+
}
void DrasculaEngine::updateScreen(int xorg, int yorg, int xdes, int ydes, int width, int height, byte *buffer) {
- byte *screenBuffer = (byte *)_system->lockScreen()->pixels;
- copyBackground(xorg, yorg, xdes, ydes, width, height, buffer, screenBuffer);
- _system->unlockScreen();
+ _system->copyRectToScreen(buffer + xorg + yorg * 320, 320, xdes, ydes, width, height);
_system->updateScreen();
}
diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp
index 21803a8932..1495694a1b 100644
--- a/engines/drascula/interface.cpp
+++ b/engines/drascula/interface.cpp
@@ -65,6 +65,8 @@ void DrasculaEngine::selectVerbFromBar() {
}
void DrasculaEngine::selectVerb(int verb) {
+ debug(4, "selectVerb(%d)", verb);
+
int c = _menuScreen ? 0 : 171;
if (currentChapter == 5) {
@@ -76,7 +78,7 @@ void DrasculaEngine::selectVerb(int verb) {
}
for (int i = 0; i < OBJHEIGHT; i++)
- memcpy(mouseCursor + i * OBJWIDTH, backSurface + OBJWIDTH * verb + (c + i) * 320, OBJWIDTH);
+ memcpy(mouseCursor + i * OBJWIDTH, cursorSurface + OBJWIDTH * verb + (c + i) * 320, OBJWIDTH);
setCursor(kCursorCurrentItem);
if (verb > 0) {
@@ -126,7 +128,7 @@ void DrasculaEngine::showMenu() {
OBJWIDTH, OBJHEIGHT, srcSurface, screenSurface);
}
copyRect(_x1d_menu[h], _y1d_menu[h], _itemLocations[n].x, _itemLocations[n].y,
- OBJWIDTH, OBJHEIGHT, backSurface, screenSurface);
+ OBJWIDTH, OBJHEIGHT, cursorSurface, screenSurface);
}
if (x < 7)
@@ -140,7 +142,7 @@ void DrasculaEngine::clearMenu() {
if (mouseX > _verbBarX[n] && mouseX < _verbBarX[n + 1])
verbActivated = 0;
copyRect(OBJWIDTH * n, OBJHEIGHT * verbActivated, _verbBarX[n], 2,
- OBJWIDTH, OBJHEIGHT, backSurface, screenSurface);
+ OBJWIDTH, OBJHEIGHT, cursorSurface, screenSurface);
verbActivated = 1;
}
}
diff --git a/engines/drascula/module.mk b/engines/drascula/module.mk
index a9fa257549..20fd900124 100644
--- a/engines/drascula/module.mk
+++ b/engines/drascula/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/drascula
MODULE_OBJS := \
actors.o \
animation.o \
+ console.o \
converse.o \
detection.o \
drascula.o \
diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp
index c4dc3df1f6..08c1a68a55 100644
--- a/engines/drascula/objects.cpp
+++ b/engines/drascula/objects.cpp
@@ -78,8 +78,11 @@ void DrasculaEngine::gotoObject(int pointX, int pointY) {
for (;;) {
updateRoom();
updateScreen();
+ updateEvents();
if (characterMoved == 0)
break;
+
+ pause(3);
}
if (walkToObject == 1) {
diff --git a/engines/drascula/palette.cpp b/engines/drascula/palette.cpp
index 1e51deffd9..0f75bb7959 100644
--- a/engines/drascula/palette.cpp
+++ b/engines/drascula/palette.cpp
@@ -106,6 +106,8 @@ void DrasculaEngine::fadeToBlack(int fadeSpeed) {
pause(fadeSpeed);
setPalette((byte *)&palFade);
+
+ updateEvents();
}
}
@@ -124,6 +126,8 @@ void DrasculaEngine::fadeFromBlack(int fadeSpeed) {
pause(fadeSpeed);
setPalette((byte *)&palFade);
+
+ updateEvents();
}
}
diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp
index a71545feca..57bfad26af 100644
--- a/engines/drascula/rooms.cpp
+++ b/engines/drascula/rooms.cpp
@@ -1091,7 +1091,7 @@ void DrasculaEngine::updateRefresh() {
sprintf(rm, "update_%d", roomNumber);
for (uint i = 0; i < _roomHandlers->roomUpdaters.size(); i++) {
if (!strcmp(rm, _roomHandlers->roomUpdaters[i]->desc)) {
- debug(4, "Calling room updater %d", roomNumber);
+ debug(8, "Calling room updater %d", roomNumber);
(this->*(_roomHandlers->roomUpdaters[i]->proc))();
break;
}
@@ -1129,7 +1129,7 @@ void DrasculaEngine::updateRefresh_pre() {
sprintf(rm, "update_%d_pre", roomNumber);
for (uint i = 0; i < _roomHandlers->roomPreupdaters.size(); i++) {
if (!strcmp(rm, _roomHandlers->roomPreupdaters[i]->desc)) {
- debug(4, "Calling room preupdater %d", roomNumber);
+ debug(8, "Calling room preupdater %d", roomNumber);
(this->*(_roomHandlers->roomPreupdaters[i]->proc))();
break;
}
diff --git a/engines/drascula/talk.cpp b/engines/drascula/talk.cpp
index 54175c5e5b..8cefe0385c 100644
--- a/engines/drascula/talk.cpp
+++ b/engines/drascula/talk.cpp
@@ -170,6 +170,7 @@ void DrasculaEngine::talk_drascula(int index, int talkerType) {
centerText(said, drasculaX + 19, drasculaY);
updateScreen();
+ updateEvents();
pause(3);
@@ -215,6 +216,7 @@ void DrasculaEngine::talk_drascula_big(int index) {
centerText(said, 191, 69);
updateScreen();
+ updateEvents();
pause(3);
@@ -245,7 +247,9 @@ void DrasculaEngine::talk_solo(const char *said, const char *filename) {
else if (currentChapter == 5)
centerText(said, 173, 92);
}
+ updateEvents();
updateScreen();
+ pause(3);
} while (!isTalkFinished());
if (currentChapter == 6) {
@@ -304,6 +308,7 @@ void DrasculaEngine::talk_bartender(int index, int talkerType) {
centerText(said, 132, 45);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -331,11 +336,9 @@ void DrasculaEngine::talk_bj(int index) {
updateRefresh_pre();
- copyBackground(bjX + 2, bjY - 1, bjX + 2, bjY - 1, 27, 40,
- bgSurface, screenSurface);
+ copyBackground(bjX + 2, bjY - 1, bjX + 2, bjY - 1, 27, 40, bgSurface, screenSurface);
- copyRect(x_talk[face], 99, bjX + 2, bjY - 1, 27, 40,
- drawSurface3, screenSurface);
+ copyRect(x_talk[face], 99, bjX + 2, bjY - 1, 27, 40, drawSurface3, screenSurface);
moveCharacters();
updateRefresh();
@@ -353,6 +356,7 @@ void DrasculaEngine::talk_bj(int index) {
updateScreen();
}
+ updateEvents();
} while (!isTalkFinished());
updateRoom();
@@ -467,6 +471,7 @@ void DrasculaEngine::talk(const char *said, const char *filename) {
centerText(said, curX, curY);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -558,16 +563,15 @@ void DrasculaEngine::talk_vonBraun(int index, int talkerType) {
if (!_subtitlesDisabled)
centerText(said, vonBraunX, 66);
- updateScreen();
- pause(3);
} else {
updateRoom();
if (!_subtitlesDisabled)
centerText(said, 150, 80);
-
- updateScreen();
}
+ updateScreen();
+ updateEvents();
+ pause(3);
} while (!isTalkFinished());
updateRoom();
@@ -621,6 +625,7 @@ void DrasculaEngine::talk_blind(int index) {
centerText(said, 260, 71);
updateScreen();
+ updateEvents();
pause(2);
p++;
} while (!isTalkFinished());
@@ -641,7 +646,9 @@ void DrasculaEngine::talk_hacker(int index) {
do {
if (!_subtitlesDisabled)
centerText(said, 156, 170);
+ updateEvents();
updateScreen();
+ pause(3);
} while (!isTalkFinished());
}
@@ -693,13 +700,13 @@ void DrasculaEngine::talk_pen(const char *said, const char *filename, int talker
copyBackground();
updateRefresh_pre();
+ updateRefresh();
+
if (talkerType == 0)
copyRect(x_talk[face], 145, 145, 105, 25, 29, drawSurface3, screenSurface);
else
copyBackground(x_talk2[face], 171, 173, 116, 25, 28, drawSurface3, screenSurface);
- updateRefresh();
-
if (!_subtitlesDisabled) {
if (talkerType == 0)
centerText(said, 160, 105);
@@ -708,6 +715,7 @@ void DrasculaEngine::talk_pen(const char *said, const char *filename, int talker
}
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -745,6 +753,7 @@ void DrasculaEngine::talk_bj_bed(int index) {
centerText(said, 104, 102);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -781,6 +790,7 @@ void DrasculaEngine::talk_htel(int index) {
centerText(said, 90, 50);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -862,6 +872,7 @@ void DrasculaEngine::talk_sync(const char *said, const char *filename, const cha
centerText(said, curX, curY);
updateScreen();
+ updateEvents();
p++;
pause(3);
@@ -895,6 +906,7 @@ void DrasculaEngine::talk_trunk(int index) {
centerText(said, 263, 69);
updateScreen();
+ updateEvents();
pause(4);
} while (!isTalkFinished());
@@ -922,6 +934,7 @@ void DrasculaEngine::talk_generic(const char* said, const char* filename, int* f
centerText(said, coords[5], coords[6]);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -944,8 +957,10 @@ void DrasculaEngine::grr() {
updateScreen();
- while (!isTalkFinished())
- ;
+ while (!isTalkFinished()) {
+ updateEvents();
+ pause(3);
+ }
updateRoom();
updateScreen();
diff --git a/engines/engine.cpp b/engines/engine.cpp
index 0f42cd493d..e2c0bb79f3 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -23,6 +23,7 @@
*/
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <direct.h>
// winnt.h defines ARRAYSIZE, but we want our own one...
@@ -77,7 +78,7 @@ static void defaultErrorHandler(const char *msg) {
if (isSmartphone())
debugger = 0;
#endif
- if (debugger && !debugger->isAttached()) {
+ if (debugger && !debugger->isActive()) {
debugger->attach(msg);
debugger->onFrame();
}
diff --git a/engines/game.cpp b/engines/game.cpp
index c7f26019d6..dea6d37485 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -73,6 +73,10 @@ void GameDescriptor::setGUIOptions(uint32 guioptions) {
erase("guioptions");
}
+void GameDescriptor::appendGUIOptions(const Common::String &str) {
+ setVal("guioptions", getVal("guioptions", "") + " " + str);
+}
+
void GameDescriptor::updateDesc(const char *extra) {
// TODO: The format used here (LANG/PLATFORM/EXTRA) is not set in stone.
// We may want to change the order (PLATFORM/EXTRA/LANG, anybody?), or
diff --git a/engines/game.h b/engines/game.h
index 49136ecf5a..b125421ff6 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -83,6 +83,7 @@ public:
void updateDesc(const char *extra = 0);
void setGUIOptions(uint32 options);
+ void appendGUIOptions(const Common::String &str);
Common::String &gameid() { return getVal("gameid"); }
Common::String &description() { return getVal("description"); }
diff --git a/engines/gob/demos/demoplayer.cpp b/engines/gob/demos/demoplayer.cpp
index 38e20a46ee..25cd470f04 100644
--- a/engines/gob/demos/demoplayer.cpp
+++ b/engines/gob/demos/demoplayer.cpp
@@ -152,12 +152,13 @@ void DemoPlayer::playVideo(const char *fileName) {
debugC(1, kDebugDemo, "Playing video \"%s\"", file);
- int16 x = _rebase0 ? 0 : -1;
- int16 y = _rebase0 ? 0 : -1;
- if (_vm->_vidPlayer->primaryOpen(file, x, y)) {
- bool videoSupportsDouble =
- ((_vm->_vidPlayer->getFeatures() & Graphics::CoktelVideo::kFeaturesSupportsDouble) != 0);
+ VideoPlayer::Properties props;
+ props.x = _rebase0 ? 0 : -1;
+ props.y = _rebase0 ? 0 : -1;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, file, props)) >= 0) {
if (_autoDouble) {
int16 defX = _rebase0 ? 0 : _vm->_vidPlayer->getDefaultX();
int16 defY = _rebase0 ? 0 : _vm->_vidPlayer->getDefaultY();
@@ -167,16 +168,12 @@ void DemoPlayer::playVideo(const char *fileName) {
_doubleMode = ((right < 320) && (bottom < 200));
}
- if (_doubleMode) {
- if (videoSupportsDouble) {
- _vm->_vidPlayer->slotSetDoubleMode(-1, true);
- playVideoNormal();
- } else
- playVideoDoubled();
- } else
- playVideoNormal();
+ if (_doubleMode)
+ playVideoDoubled(slot);
+ else
+ playVideoNormal(slot);
- _vm->_vidPlayer->primaryClose();
+ _vm->_vidPlayer->closeVideo(slot);
if (waitTime > 0)
_vm->_util->longDelay(waitTime);
@@ -210,51 +207,67 @@ void DemoPlayer::playADL(const char *params) {
playADL(fileName, waitEsc, repeat);
}
-void DemoPlayer::playVideoNormal() {
- _vm->_vidPlayer->primaryPlay();
+void DemoPlayer::playVideoNormal(int slot) {
+ VideoPlayer::Properties props;
+
+ _vm->_vidPlayer->play(slot, props);
}
-void DemoPlayer::playVideoDoubled() {
- Common::String fileNameOpened = _vm->_vidPlayer->getFileName();
- _vm->_vidPlayer->primaryClose();
+void DemoPlayer::playVideoDoubled(int slot) {
+ Common::String fileNameOpened = _vm->_vidPlayer->getFileName(slot);
+ _vm->_vidPlayer->closeVideo(slot);
- int16 x = _rebase0 ? 0 : -1;
- int16 y = _rebase0 ? 0 : -1;
- if (_vm->_vidPlayer->primaryOpen(fileNameOpened.c_str(), x, y,
- VideoPlayer::kFlagScreenSurface)) {
+ VideoPlayer::Properties props;
- for (int i = 0; i < _vm->_vidPlayer->getFramesCount(); i++) {
- _vm->_vidPlayer->playFrame(i);
+ props.x = _rebase0 ? 0 : -1;
+ props.y = _rebase0 ? 0 : -1;
+ props.flags = VideoPlayer::kFlagScreenSurface;
+ props.waitEndFrame = false;
- Graphics::CoktelVideo::State state = _vm->_vidPlayer->getState();
+ _vm->_vidPlayer->evaluateFlags(props);
+
+ slot = _vm->_vidPlayer->openVideo(true, fileNameOpened, props);
+ if (slot < 0)
+ return;
- int16 w = state.right - state.left + 1;
- int16 h = state.bottom - state.top + 1;
- int16 wD = (state.left * 2) + (w * 2);
- int16 hD = (state.top * 2) + (h * 2);
+ for (uint i = 0; i < _vm->_vidPlayer->getFrameCount(slot); i++) {
+ props.startFrame = _vm->_vidPlayer->getCurrentFrame(slot) + 1;
+ props.lastFrame = _vm->_vidPlayer->getCurrentFrame(slot) + 1;
+
+ _vm->_vidPlayer->play(slot, props);
+
+ const Common::List<Common::Rect> *rects = _vm->_vidPlayer->getDirtyRects(slot);
+ if (rects) {
+ for (Common::List<Common::Rect>::const_iterator rect = rects->begin(); rect != rects->end(); ++rect) {
+ int16 w = rect->right - rect->left;
+ int16 h = rect->bottom - rect->top;
+ int16 wD = (rect->left * 2) + (w * 2);
+ int16 hD = (rect->top * 2) + (h * 2);
_vm->_video->drawSpriteDouble(*_vm->_draw->_spritesArray[0], *_vm->_draw->_frontSurface,
- state.left, state.top, state.right, state.bottom, state.left, state.top, 0);
+ rect->left, rect->top, rect->right - 1, rect->bottom - 1, rect->left, rect->top, 0);
_vm->_draw->dirtiedRect(_vm->_draw->_frontSurface,
- state.left * 2, state.top * 2, wD, hD);
- _vm->_video->retrace();
+ rect->left * 2, rect->top * 2, wD, hD);
+ }
+ }
- _vm->_util->processInput();
- if (_vm->shouldQuit())
- break;
+ _vm->_video->retrace();
- int16 key;
- bool end = false;
- while (_vm->_util->checkKey(key))
- if (key == kKeyEscape)
- end = true;
- if (end)
- break;
+ _vm->_util->processInput();
+ if (_vm->shouldQuit())
+ break;
- _vm->_vidPlayer->slotWaitEndFrame();
+ int16 key;
+ bool end = false;
+ while (_vm->_util->checkKey(key))
+ if (key == kKeyEscape)
+ end = true;
+ if (end)
+ break;
- }
+ _vm->_vidPlayer->waitEndFrame(slot);
}
+
}
void DemoPlayer::playADL(const Common::String &fileName, bool waitEsc, int32 repeat) {
diff --git a/engines/gob/demos/demoplayer.h b/engines/gob/demos/demoplayer.h
index f0672b9645..207b050363 100644
--- a/engines/gob/demos/demoplayer.h
+++ b/engines/gob/demos/demoplayer.h
@@ -59,8 +59,8 @@ protected:
void playVideo(const char *fileName);
void playADL(const char *params);
- void playVideoNormal();
- void playVideoDoubled();
+ void playVideoNormal(int slot);
+ void playVideoDoubled(int slot);
void playADL(const Common::String &fileName, bool waitEsc = true, int32 repeat = -1);
private:
diff --git a/engines/gob/detection.cpp b/engines/gob/detection.cpp
index 1f8bfdc138..a1eb8055aa 100644
--- a/engines/gob/detection.cpp
+++ b/engines/gob/detection.cpp
@@ -87,4994 +87,7 @@ static const ADObsoleteGameID obsoleteGameIDsTable[] = {
{0, 0, kPlatformUnknown}
};
-namespace Gob {
-
-using Common::GUIO_NOSPEECH;
-using Common::GUIO_NOSUBTITLES;
-using Common::GUIO_NONE;
-
-static const GOBGameDescription gameDescriptions[] = {
- { // Supplied by Florian Zeitz on scummvm-devel
- {
- "gob1",
- "EGA",
- AD_ENTRY1("intro.stk", "c65e9cc8ba23a38456242e1f2b1caad4"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "EGA",
- AD_ENTRY1("intro.stk", "f9233283a0be2464248d83e14b95f09c"),
- RU_RUS,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesEGA,
- 0, 0, 0
- },
- { // Supplied by Theruler76 in bug report #1201233
- {
- "gob1",
- "VGA",
- AD_ENTRY1("intro.stk", "26a9118c0770fa5ac93a9626761600b2"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by raziel_ in bug report #1891864
- {
- "gob1",
- "VGA",
- AD_ENTRY1s("intro.stk", "e157cb59c6d330ca70d12ab0ef1dd12b", 288972),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by raina in the forums
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "6d837c6380d8f4d984c9f6cc0026df4f", 192712),
- EN_ANY,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- EN_ANY,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- DE_DEU,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- FR_FRA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- IT_ITA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- ES_ESP,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- HU_HUN,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "Demo",
- AD_ENTRY1("intro.stk", "972f22c6ff8144a6636423f0354ca549"),
- UNK_LANG,
- kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "e72bd1e3828c7dec4c8a3e58c48bdfdb"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "Interactive Demo",
- AD_ENTRY1s("intro.stk", "a796096280d5efd48cf8e7dfbe426eb5", 193595),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2785958
- {
- "gob1",
- "Interactive Demo",
- AD_ENTRY1s("intro.stk", "35a098571af9a03c04e2303aec7c9249", 116582),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "0e022d3f2481b39e9175d37b2c6ad4c6", 2390121),
- FR_FRA,
- kPlatformCDi,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, "AVT003.TOT", 0
- },
- { // Supplied by fac76 in bug report #1883808
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "eebf2810122cfd17399260cd1468e994", 554014),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "d28b9e9b41f31acfa58dcd12406c7b2c"),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2602057
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "686c88f7302a80b744aae9f8413e853d"),
- IT_ITA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by bgk in bug report #1706861
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "4b13c02d1069b86bcfec80f4e474b98b", 554680),
- FR_FRA,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by fac76 in bug report #1673397
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "b45b984ee8017efd6ea965b9becd4d66", 828443},
- {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
- {0, 0, 0, 0}
- },
- UNK_LANG,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by koalet in bug report #2478585
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275},
- {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "b45b984ee8017efd6ea965b9becd4d66"),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "dedb5d31d8c8050a8cf77abedcc53dae"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by raziel_ in bug report #1891867
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "25a99827cd59751a80bed9620fb677a0", 893302),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by blackwhiteeagle in bug report #1605235
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "3e4e7db0d201587dd2df4003b2993ef6"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "a13892cdf4badda85a6f6fb47603a128"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2602017
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "c47faf1d406504e6ffe63243610bb1f4"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "cd3e1df8b273636ee32e34b7064f50e8"),
- RU_RUS,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by arcepi in bug report #1659884
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "5f53c56e3aa2f1e76c2e4f0caa15887f", 829232),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "285d7340f98ebad65d465585da12910b", 837286},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- EN_USA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- EN_USA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "24489330a1d67ff978211f574822a5a6", 883756),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "285d7340f98ebad65d465585da12910b", 837286),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "9de5fbb41cf97182109e5fecc9d90347"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- HU_HUN,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "Non-Interactive Demo",
- AD_ENTRY1("intro.stk", "8b1c98ff2ab2e14f47a1b891e9b92217"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, "usa.tot", 0
- },
- {
- {
- "gob2",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "cf1c95b2939bd8ff58a25c756cb6125e"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "4b278c2678ea01383fd5ca114d947eea"),
- UNK_LANG,
- kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by polluks in bug report #1895126
- {
- "gob2",
- "Interactive Demo",
- AD_ENTRY1s("intro.stk", "9fa85aea959fa8c582085855fbd99346", 553063),
- UNK_LANG,
- kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- {
- {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
- {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "257fe669705ac4971efdfd5656eef16a", 457719),
- FR_FRA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "dffd1ab98fe76150d6933329ca6f4cc4", 459458),
- FR_FRA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "af83debf2cbea21faa591c7b4608fe92", 458192),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2563539
- {
- "ween",
- "",
- {
- {"intro.stk", 0, "dffd1ab98fe76150d6933329ca6f4cc4", 459458},
- {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by pwigren in bug report #1764174
- {
- "ween",
- "",
- {
- {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
- {"music__5.snd", 0, "7d1819b9981ecddd53d3aacbc75f1cc8", 13446},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "e6d13fb3b858cb4f78a8780d184d5b2c"),
- FR_FRA,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "2bb8878a8042244dd2b96ff682381baa"),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "de92e5c6a8c163007ffceebef6e67f7d", 7117568),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by cybot_tmin in bug report #1667743
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "6d60f9205ecfbd8735da2ee7823a70dc", 7014426),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "4b10525a3782aa7ecd9d833b5c1d308b"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by cartman_ on #scummvm
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "63170e71f04faba88673b3f510f9c4c8"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by glorfindel in bugreport #1722142
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "8b57cd510da8a3bbd99e3a0297a8ebd1", 7018771),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "ween",
- "Demo",
- AD_ENTRY1("intro.stk", "2e9c2898f6bf206ede801e3b2e7ee428"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, "show.tot", 0
- },
- {
- {
- "ween",
- "Demo",
- AD_ENTRY1("intro.stk", "15fb91a1b9b09684b28ac75edf66e504"),
- EN_USA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, "show.tot", 0
- },
- {
- {
- "bargon",
- "",
- AD_ENTRY1("intro.stk", "da3c54be18ab73fbdb32db24624a9c23"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by Trekky in the forums
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "2f54b330d21f65b04b7c1f8cca76426c", 262109),
- FR_FRA,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by cesardark in bug #1681649
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "11103b304286c23945560b391fd37e7d", 3181890),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug #1692667
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by pwigren in bugreport #1764174
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "569d679fe41d49972d34c9fce5930dda", 269825),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by kizkoool in bugreport #2089734
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "00f6b4e2ee26e5c40b488e2df5adcf03", 3975580),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by glorfindel in bugreport #1722142
- {
- "bargon",
- "Fanmade",
- AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- {
- {"intro.stk", 0, "0b72992f5d8b5e6e0330572a5753ea25", 256490},
- {"mod.babayaga", 0, "43484cde74e0860785f8e19f0bc776d1", 60248},
- {0, 0, 0, 0}
- },
- UNK_LANG,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "7b7f48490dedc8a7cb999388e2fadbe3", 3930674),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "e0767783ff662ed93665446665693aef", 4371238),
- HE_ISR,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by cartman_ on #scummvm
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "f1f78b663893b58887add182a77df151", 3944090),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2105220
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "cd322cb3c64ef2ba2f2134aa2122cfe9", 3936700),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by koalet in bug report #2479034
- {
- "lit",
- "",
- {
- {"intro.stk", 0, "af98bcdc70e1f1c1635577fd726fe7f1", 3937310},
- {"musmac1.mid", 0, "ae7229bb09c6abe4e60a2768b24bc890", 9398},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in french ADI 2.6 Francais-Maths 4e
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "58ee9583a4fb837f02d9a58e5f442656", 3937120),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit1",
- "Full install",
- {
- {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
- {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 4396644},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit1",
- "Light install",
- {
- {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
- {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 664064},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit2",
- "Light install",
- AD_ENTRY1s("intro.stk", "17acbb212e62addbe48dc8f2282c98cb", 72318),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit2",
- "Full install",
- {
- {"intro.stk", 0, "17acbb212e62addbe48dc8f2282c98cb", 72318},
- {"partie4.itk", 0, "6ce4967e0c79d7daeabc6c1d26783d4c", 2612087},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "Demo",
- AD_ENTRY1("demo.stk", "c06f8cc20eb239d4c71f225ce3093edf"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- "demo.stk", "demo.tot", 0
- },
- {
- {
- "lit",
- "Non-interactive Demo",
- AD_ENTRY1("demo.stk", "2eba8abd9e3878c57307576012dd2fec"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- "demo.stk", "demo.tot", 0
- },
- {
- {
- "fascination",
- "CD Version (Censored)",
- AD_ENTRY1s("disk0.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES
- },
- kGameTypeFascination,
- kFeaturesCD,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("disk0.stk", "a50a8495e1b2d67699fb562cb98fc3e2", 1064387),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("intro.stk", "d6e45ce548598727e2b5587a99718eba", 1055909),
- HE_ISR,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "intro.stk", 0, 0
- },
- { // Supplied by sanguine
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("disk0.stk", "c14330d052fe4da5a441ac9d81bc5891", 1061955),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- { // Supplied by windlepoons in bug report #2809247
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("disk0.stk", "3a24e60a035250189643c86a9ceafb97", 1062480),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "VGA",
- AD_ENTRY1s("disk0.stk", "e8ab4f200a2304849f462dc901705599", 183337),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "68b1c01564f774c0b640075fbad1b695", 189968),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "7062117e9c5adfb6bfb2dac3ff74df9e", 189951),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "55c154e5a3e8e98afebdcff4b522e1eb", 190005),
- FR_FRA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "7691827fff35df7799f14cfd6be178ad", 189931),
- IT_ITA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "aff9fcc619f4dd19eae228affd0d34c8", 189964),
- EN_ANY,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "geisha",
- "",
- AD_ENTRY1s("disk1.stk", "6eebbb98ad90cd3c44549fc2ab30f632", 212153),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGeisha,
- kFeaturesNone,
- "disk1.stk", "intro.tot", 0
- },
- {
- {
- "geisha",
- "",
- AD_ENTRY1s("disk1.stk", "f4d4d9d20f7ad1f879fc417d47faba89", 336732),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGeisha,
- kFeaturesNone,
- "disk1.stk", "intro.tot", 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "32b0f57f5ae79a9ae97e8011df38af42", 157084),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "904fc32032295baa3efb3a41f17db611", 178582),
- HE_ISR,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by raziel_ in bug report #1891869
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "16b014bf32dbd6ab4c5163c44f56fed1", 445104),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by fac76 in bug report #1742716
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "32b0f57f5ae79a9ae97e8011df38af42", 157084},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "1e2f64ec8dfa89f42ee49936a27e66e7"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "f6d225b25a180606fa5dbe6405c97380"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "e42a4f2337d6549487a80864d7826972"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Paranoimia on #scummvm
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "fe8144daece35538085adb59c2d29613", 159402),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "4e3af248a48a2321364736afab868527"),
- RU_RUS,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "8d28ce1591b0e9cc79bf41cad0fc4c9c"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2098621
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "d3b72938fbbc8159198088811f9e6d19", 160382),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
- EN_GRB,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesNone,
- 0, "menu.tot", 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesNone,
- 0, "menu.tot", 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "edd7403e5dc2a14459d2665a4c17714d", 209534},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "edd7403e5dc2a14459d2665a4c17714d", 209534),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "6f2c226c62dd7ab0ab6f850e89d3fc47"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- HU_HUN,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "7aebd94e49c2c5c518c9e7b74f25de9d"),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Interactive Demo 2",
- AD_ENTRY1("intro.stk", "e5dcbc9f6658ebb1e8fe26bc4da0806d"),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Interactive Demo 3",
- AD_ENTRY1s("intro.stk", "9e20ad7b471b01f84db526da34eaf0a2", 395561),
- EN_ANY,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Non-interactive Demo",
- AD_ENTRY1("intro.stk", "b9b898fccebe02b69c086052d5024a55"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- EN_USA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "Non-Interactive Demo",
- {
- {"cons.imd", 0, "f896ba0c4a1ac7f7260d342655980b49", 17804},
- {"conseil.imd", 0, "aaedd5482d5b271e233e86c5a03cf62e", 33999},
- {"int.imd", 0, "6308222fcefbcb20925f01c1aff70dee", 30871},
- {"inter.imd", 0, "39bd6d3540f3bedcc97293f352c7f3fc", 191719},
- {"machu.imd", 0, "c0bc8211d93b467bfd063b63fe61b85c", 34609},
- {"post.imd", 0, "d75cad0e3fc22cb0c8b6faf597f509b2", 1047709},
- {"posta.imd", 0, "2a5b3fe75681ddf4d21ac724db8111b4", 547250},
- {"postb.imd", 0, "24260ce4e80a4c472352b76637265d09", 868312},
- {"postc.imd", 0, "24accbcc8b83a9c2be4bd82849a2bd29", 415637},
- {"tum.imd", 0, "0993d4810ec9deb3f77c5e92095320fd", 20330},
- {"tumi.imd", 0, "bf53f229480d694de0947fe3366fbec6", 248952},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib | kFeaturesBATDemo,
- 0, 0, 7
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "5f5f4e0a72c33391e67a47674b120cc6", 20296422),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2098838
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "08a96bf061af1fa4f75c6a7cc56b60a4", 20734979),
- PL_POL,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "Non-Interactive Demo",
- {
- {"demo.scn", 0, "16bb85fc5f8e519147b60475dbf33962", 89},
- {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 1
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "6190e32404b672f4bbbc39cf76f41fda", 2511470),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "b3f8472484b7a1df94557b51e7b6fca0", 2322644),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "bdbdac8919200a5e71ffb9fb0709f704", 2446652),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "Demo",
- AD_ENTRY1s("intro.stk", "464538a17ed39755d7f1ba9c751af1bd", 1847864),
- EN_USA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "Demo",
- AD_ENTRY1s("lda1.stk", "0e56a899357cbc0bf503260fd2dd634e", 15032774),
- UNK_LANG,
- kPlatformWindows,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeDynasty,
- kFeatures640,
- "lda1.stk", 0, 0
- },
- {
- {
- "dynasty",
- "Demo",
- AD_ENTRY1s("lda1.stk", "8669ea2e9a8239c070dc73958fbc8753", 15567724),
- DE_DEU,
- kPlatformWindows,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeDynasty,
- kFeatures640,
- "lda1.stk", 0, 0
- },
- {
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "3ab2c542bd9216ae5d02cc6f45701ae1", 1252436),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by gamin in the forums
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "b991ed1d31c793e560edefdb349882ef", 1276408),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "4ec3c0864e2b54c5b4ccf9f6ad96528d", 1253328),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2770340
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "4bd31979ea3d77a58a358c09000a85ed", 1253018),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "urban",
- "Non-Interactive Demo",
- {
- {"wdemo.s24", 0, "14ac9bd51db7a075d69ddb144904b271", 87},
- {"demo.vmd", 0, "65d04715d871c292518b56dd160b0161", 9091237},
- {"urband.vmd", 0, "60343891868c91854dd5c82766c70ecc", 922461},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeUrban,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 2
- },
- {
- {
- "playtoons1",
- "",
- {
- {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
- {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons1",
- "Pack mes histoires anim\xE9""es",
- {
- {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
- {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons1",
- "",
- {
- {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
- {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { // Supplied by scoriae in the forums
- {
- "playtoons1",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"archi.stk", 0, "00d8274519dfcf8a0d8ae3099daea0f8", 5532135},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"play123.scn", 0, "4689a31f543915e488c3bc46ea358add", 258},
- {"archi.vmd", 0, "a410fcc8116bc173f038100f5857191c", 5617210},
- {"chato.vmd", 0, "5a10e39cb66c396f2f9d8fb35e9ac016", 5445937},
- {"genedeb.vmd", 0, "3bb4a45585f88f4d839efdda6a1b582b", 1244228},
- {"generik.vmd", 0, "b46bdd64b063e86927fb2826500ad512", 603242},
- {"genespi.vmd", 0, "b7611916f32a370ae9832962fc17ef72", 758719},
- {"spirou.vmd", 0, "8513dbf7ac51c057b21d371d6b217b47", 2550788},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 3
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"e.scn", 0, "8a0db733c3f77be86e74e8242e5caa61", 124},
- {"demarchg.vmd", 0, "d14a95da7d8792faf5503f649ffcbc12", 5619415},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 4
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"i.scn", 0, "8b3294474d39970463663edd22341730", 285},
- {"demarita.vmd", 0, "84c8672b91c7312462603446e224bfec", 5742533},
- {"dembouit.vmd", 0, "7a5fdf0a4dbdfe72e31dd489ea0f8aa2", 3536786},
- {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
- {"demoita.vmd", 0, "b4c0622d14c8749965cd0f5dfca4cf4b", 1183566},
- {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 5
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"s.scn", 0, "1f527010626b5490761f16ba7a6f639a", 251},
- {"demaresp.vmd", 0, "3f860f944056842b35a5fd05416f208e", 5720619},
- {"demboues.vmd", 0, "3a0caa10c98ef92a15942f8274075b43", 3535838},
- {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
- {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
- {0, 0, 0, 0}
- },
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 6
- },
- {
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
- {"spirou.stk", 0, "5d9c7644d0c47840169b4d016765cc1a", 9816201},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "55a85036dd93cce93532d8f743d90074", 17467154},
- {"spirou.stk", 0, "e3e1b6148dd72fafc3637f1a8e5764f5", 9812043},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
- {"spirou.stk", 0, "91080dc148de1bbd6a97321c1a1facf3", 9817086},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { // Supplied by scoriae in the forums
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"spirou.stk", 0, "993737f112ca6a9b33c814273280d832", 9825760},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
- {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"chato.stk", 0, "8fc8d0da5b3e758908d1d7298d497d0b", 6041026},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "Pack mes histoires anim\xE9""es",
- {
- {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
- {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
- {"chato.stk", 0, "3c6cb3ac8a5a7cf681a19971a92a748d", 6033791},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
- {"chato.stk", 0, "bdef407387112bfcee90e664865ac3af", 6033867},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons4",
- "",
- {
- {"playtoon.stk", 0, "b7f5afa2dc1b0f75970b7c07d175db1b", 24340406},
- {"manda.stk", 0, "92529e0b927191d9898a34c2892e9a3a", 6485072},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { //Supplied by goodoldgeorg in bug report #2820006
- {
- "playtoons4",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"manda.stk", 0, "69a79c9f61b2618e482726f2ff68078d", 6499208},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons5",
- "",
- {
- {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
- {"wakan.stk", 0, "f493bf82851bc5ba74d57de6b7e88df8", 5520153},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "bambou",
- "",
- {
- {"intro.stk", 0, "2f8db6963ff8d72a8331627ebda918f4", 3613238},
- {"bambou.itk", 0, "0875914d31126d0749313428f10c7768", 114440192},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBambou,
- kFeatures640,
- "intro.stk", "intro.tot", 0
- },
- {
- {
- "playtnck1",
- "",
- {
- {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
- {"dan.itk", 0, "906d67b3e438d5e95ec7ea9e781a94f3", 3000320},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtnck2",
- "",
- {
- {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
- {"dan.itk", 0, "74eeb075bd2cb47b243349730264af01", 3213312},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtnck3",
- "",
- {
- {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
- {"dan.itk", 0, "9a8f62809eca5a52f429b5b6a8e70f8f", 2861056},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "adi2",
- "Adi 2.0 for Teachers",
- AD_ENTRY1s("adi2.stk", "da6f1fb68bff32260c5eecdf9286a2f5", 1533168),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeaturesNone,
- "adi2.stk", "ediintro.tot", 0
- },
- { // Found in french ADI 2 Francais-Maths CM1. Exact version not specified.
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "23f279615c736dc38320f1348e70c36e", 10817668),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- { // Found in french ADI 2 Francais-Maths CE2. Exact version not specified.
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "d4162c4298f9423ecc1fb04965557e90", 11531214),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "29694c5a649298a42f87ae731d6d6f6d", 311132),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeaturesNone,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "2a40bb48ccbd4e6fb3f7f0fc2f069d80", 17720132),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.5",
- AD_ENTRY1s("adi2.stk", "fcac60e6627f37aee219575b60859de9", 16944268),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.5",
- AD_ENTRY1s("adi2.stk", "072d5e2d7826a7c055865568ebf918bb", 16934596),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.6",
- AD_ENTRY1s("adi2.stk", "2fb940eb8105b12871f6b88c8c4d1615", 16780058),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.6",
- AD_ENTRY1s("adi2.stk", "fde7d98a67dbf859423b6473796e932a", 18044780),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.7.1",
- AD_ENTRY1s("adi2.stk", "6fa5dffebf5c7243c6af6b8c188ee00a", 19278008),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Non-Interactive Demo",
- {
- {"demo.scn", 0, "8b5ba359fd87d586ad39c1754bf6ea35", 168},
- {"demadi2t.vmd", 0, "08a1b18cfe2015d3b43270da35cc813d", 7250723},
- {"demarch.vmd", 0, "4c4a4616585d40ef3df209e3c3911062", 5622731},
- {"demobou.vmd", 0, "2208b9855775564d15c4a5a559da0aec", 3550511},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdi2,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 1
- },
- {
- {
- "adi4",
- "Addy 4 Grundschule Basis CD",
- AD_ENTRY1s("intro.stk", "d2f0fb8909e396328dc85c0e29131ba8", 5847588),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Addy 4 Sekundarstufe Basis CD",
- AD_ENTRY1s("intro.stk", "367340e59c461b4fa36651cd74e32c4e", 5847378),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0",
- AD_ENTRY1s("intro.stk", "a3c35d19b2d28ea261d96321d208cb5a", 6021466),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0",
- AD_ENTRY1s("intro.stk", "44491d85648810bc6fcf84f9b3aa47d5", 5834944),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0",
- AD_ENTRY1s("intro.stk", "29374c0e3c10b17dd8463b06a55ad093", 6012072),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0 Limited Edition",
- AD_ENTRY1s("intro.stk", "ebbbc5e28a4adb695535ed989c1b8d66", 5929644),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.10",
- AD_ENTRY1s("intro.stk", "3e3fa9656e37d802027635ace88c4cc5", 5359144),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.10",
- AD_ENTRY1s("intro.stk", "6afc2590856433b9f5295b032f2b205d", 5923112),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.11",
- AD_ENTRY1s("intro.stk", "6296e4be4e0c270c24d1330881900c7f", 5921234),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Addy 4.21",
- AD_ENTRY1s("intro.stk", "534f0b674cd4830df94a9c32c4ea7225", 6878034),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.21",
- AD_ENTRY1s("intro.stk", "c5b9f6222c0b463f51dab47317c5b687", 5950490),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0 Interactive Demo",
- AD_ENTRY1s("intro.stk", "89ace204dbaac001425c73f394334f6f", 2413102),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0 / Adibou 2 Demo",
- AD_ENTRY1s("intro.stk", "d41d8cd98f00b204e9800998ecf8427e", 0),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "ajworld",
- "",
- AD_ENTRY1s("intro.stk", "e453bea7b28a67c930764d945f64d898", 3913628),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "adibou1",
- "ADIBOU 1 Environnement 4-7 ans",
- AD_ENTRY1s("intro.stk", "6db110188fcb7c5208d9721b5282682a", 4805104),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdibou1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2",
- AD_ENTRY1s("intro.stk", "94ae7004348dc8bf99c23a9a6ef81827", 956162),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "Le Jardin Magique d'Adibou",
- AD_ENTRY1s("intro.stk", "a8ff86f3cc40dfe5898e0a741217ef27", 956328),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2",
- AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIB\xD9 2",
- AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU Version Decouverte",
- AD_ENTRY1s("intro.stk", "558c14327b79ed39214b49d567a75e33", 8737856),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.10 Environnement",
- AD_ENTRY1s("intro.stk", "f2b797819aeedee557e904b0b5ccd82e", 8736454),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.11 Environnement",
- AD_ENTRY1s("intro.stk", "7b1f1f6f6477f54401e95d913f75e333", 8736904),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.12 Environnement",
- AD_ENTRY1s("intro.stk", "1e49c39a4a3ce6032a84b712539c2d63", 8738134),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.13s Environnement",
- AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOO 2.14 Environnement",
- AD_ENTRY1s("intro.stk", "ff63637e3cb7f0a457edf79457b1c6b3", 9333874),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- { AD_TABLE_END_MARKER, kGameTypeNone, kFeaturesNone, 0, 0, 0}
-};
-
-static const GOBGameDescription fallbackDescs[] = {
- { //0
- {
- "gob1",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { //1
- {
- "gob1cd",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { //2
- {
- "gob2",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //3
- {
- "gob2mac",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //4
- {
- "gob2cd",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { //5
- {
- "bargon",
- "",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { //6
- {
- "gob3",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //7
- {
- "gob3cd",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { //8
- {
- "woodruff",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { //9
- {
- "lostintime",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //10
- {
- "lostintime",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //11
- {
- "lostintime",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { //12
- {
- "urban",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeaturesCD,
- 0, 0, 0
- },
- { //13
- {
- "playtoons1",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //14
- {
- "playtoons2",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //15
- {
- "playtoons3",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //16
- {
- "playtoons4",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //17
- {
- "playtoons5",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //18
- {
- "playtoons construction kit",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //19
- {
- "bambou",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBambou,
- kFeatures640,
- 0, 0, 0
- },
- { //20
- {
- "fascination",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- { //21
- {
- "geisha",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGeisha,
- kFeaturesNone,
- "disk1.stk", "intro.tot", 0
- },
- { //22
- {
- "adi2",
- "",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", 0, 0
- },
- { //23
- {
- "adi4",
- "",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdi4,
- kFeatures640,
- "adif41.stk", 0, 0
- },
- { //24
- {
- "coktelplayer",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeUrban,
- kFeaturesAdLib | kFeatures640 | kFeaturesSCNDemo,
- "", "", 8
- }
-};
-
-static const ADFileBasedFallback fileBased[] = {
- { &fallbackDescs[ 0], { "intro.stk", "disk1.stk", "disk2.stk", "disk3.stk", "disk4.stk", 0 } },
- { &fallbackDescs[ 1], { "intro.stk", "gob.lic", 0 } },
- { &fallbackDescs[ 2], { "intro.stk", 0 } },
- { &fallbackDescs[ 2], { "intro.stk", "disk2.stk", "disk3.stk", 0 } },
- { &fallbackDescs[ 3], { "intro.stk", "disk2.stk", "disk3.stk", "musmac1.mid", 0 } },
- { &fallbackDescs[ 4], { "intro.stk", "gobnew.lic", 0 } },
- { &fallbackDescs[ 5], { "intro.stk", "scaa.imd", "scba.imd", "scbf.imd", 0 } },
- { &fallbackDescs[ 6], { "intro.stk", "imd.itk", 0 } },
- { &fallbackDescs[ 7], { "intro.stk", "mus_gob3.lic", 0 } },
- { &fallbackDescs[ 8], { "intro.stk", "woodruff.itk", 0 } },
- { &fallbackDescs[ 9], { "intro.stk", "commun1.itk", 0 } },
- { &fallbackDescs[10], { "intro.stk", "commun1.itk", "musmac1.mid", 0 } },
- { &fallbackDescs[11], { "intro.stk", "commun1.itk", "lost.lic", 0 } },
- { &fallbackDescs[12], { "intro.stk", "cd1.itk", "objet1.itk", 0 } },
- { &fallbackDescs[13], { "playtoon.stk", "archi.stk", 0 } },
- { &fallbackDescs[14], { "playtoon.stk", "spirou.stk", 0 } },
- { &fallbackDescs[15], { "playtoon.stk", "chato.stk", 0 } },
- { &fallbackDescs[16], { "playtoon.stk", "manda.stk", 0 } },
- { &fallbackDescs[17], { "playtoon.stk", "wakan.stk", 0 } },
- { &fallbackDescs[18], { "playtoon.stk", "dan.itk" } },
- { &fallbackDescs[19], { "intro.stk", "bambou.itk", 0 } },
- { &fallbackDescs[20], { "disk0.stk", "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
- { &fallbackDescs[21], { "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
- { &fallbackDescs[22], { "adi2.stk", 0 } },
- { &fallbackDescs[23], { "adif41.stk", "adim41.stk", 0 } },
- { &fallbackDescs[24], { "coktelplayer.scn", 0 } },
- { 0, { 0 } }
-};
-
-}
+#include "gob/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -5094,7 +107,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOLAUNCHLOAD
+ Common::GUIO_NOLAUNCHLOAD,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class GobMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/gob/detection_tables.h b/engines/gob/detection_tables.h
new file mode 100644
index 0000000000..20edb9fbc3
--- /dev/null
+++ b/engines/gob/detection_tables.h
@@ -0,0 +1,5013 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+namespace Gob {
+
+using Common::GUIO_NOSPEECH;
+using Common::GUIO_NOSUBTITLES;
+using Common::GUIO_NONE;
+
+static const GOBGameDescription gameDescriptions[] = {
+ { // Supplied by Florian Zeitz on scummvm-devel
+ {
+ "gob1",
+ "EGA",
+ AD_ENTRY1("intro.stk", "c65e9cc8ba23a38456242e1f2b1caad4"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "EGA",
+ AD_ENTRY1("intro.stk", "f9233283a0be2464248d83e14b95f09c"),
+ RU_RUS,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Supplied by Theruler76 in bug report #1201233
+ {
+ "gob1",
+ "VGA",
+ AD_ENTRY1("intro.stk", "26a9118c0770fa5ac93a9626761600b2"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by raziel_ in bug report #1891864
+ {
+ "gob1",
+ "VGA",
+ AD_ENTRY1s("intro.stk", "e157cb59c6d330ca70d12ab0ef1dd12b", 288972),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by raina in the forums
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "6d837c6380d8f4d984c9f6cc0026df4f", 192712),
+ EN_ANY,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ EN_ANY,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ DE_DEU,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ FR_FRA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ IT_ITA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ ES_ESP,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ HU_HUN,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "Demo",
+ AD_ENTRY1("intro.stk", "972f22c6ff8144a6636423f0354ca549"),
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "e72bd1e3828c7dec4c8a3e58c48bdfdb"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "Interactive Demo",
+ AD_ENTRY1s("intro.stk", "a796096280d5efd48cf8e7dfbe426eb5", 193595),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2785958
+ {
+ "gob1",
+ "Interactive Demo",
+ AD_ENTRY1s("intro.stk", "35a098571af9a03c04e2303aec7c9249", 116582),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "0e022d3f2481b39e9175d37b2c6ad4c6", 2390121),
+ FR_FRA,
+ kPlatformCDi,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, "AVT003.TOT", 0
+ },
+ { // Supplied by fac76 in bug report #1883808
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "eebf2810122cfd17399260cd1468e994", 554014),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "d28b9e9b41f31acfa58dcd12406c7b2c"),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2602057
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "686c88f7302a80b744aae9f8413e853d"),
+ IT_ITA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by bgk in bug report #1706861
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "4b13c02d1069b86bcfec80f4e474b98b", 554680),
+ FR_FRA,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by fac76 in bug report #1673397
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "b45b984ee8017efd6ea965b9becd4d66", 828443},
+ {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
+ {0, 0, 0, 0}
+ },
+ UNK_LANG,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by koalet in bug report #2478585
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275},
+ {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "b45b984ee8017efd6ea965b9becd4d66"),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "dedb5d31d8c8050a8cf77abedcc53dae"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by raziel_ in bug report #1891867
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "25a99827cd59751a80bed9620fb677a0", 893302),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by blackwhiteeagle in bug report #1605235
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "3e4e7db0d201587dd2df4003b2993ef6"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "a13892cdf4badda85a6f6fb47603a128"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2602017
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "c47faf1d406504e6ffe63243610bb1f4"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "cd3e1df8b273636ee32e34b7064f50e8"),
+ RU_RUS,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by arcepi in bug report #1659884
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "5f53c56e3aa2f1e76c2e4f0caa15887f", 829232),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "285d7340f98ebad65d465585da12910b", 837286},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ EN_USA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ EN_USA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "24489330a1d67ff978211f574822a5a6", 883756),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "285d7340f98ebad65d465585da12910b", 837286),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "9de5fbb41cf97182109e5fecc9d90347"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ HU_HUN,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "Non-Interactive Demo",
+ AD_ENTRY1("intro.stk", "8b1c98ff2ab2e14f47a1b891e9b92217"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, "usa.tot", 0
+ },
+ {
+ {
+ "gob2",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "cf1c95b2939bd8ff58a25c756cb6125e"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "4b278c2678ea01383fd5ca114d947eea"),
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by polluks in bug report #1895126
+ {
+ "gob2",
+ "Interactive Demo",
+ AD_ENTRY1s("intro.stk", "9fa85aea959fa8c582085855fbd99346", 553063),
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ {
+ {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
+ {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "257fe669705ac4971efdfd5656eef16a", 457719),
+ FR_FRA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "dffd1ab98fe76150d6933329ca6f4cc4", 459458),
+ FR_FRA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "af83debf2cbea21faa591c7b4608fe92", 458192),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2563539
+ {
+ "ween",
+ "",
+ {
+ {"intro.stk", 0, "dffd1ab98fe76150d6933329ca6f4cc4", 459458},
+ {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by pwigren in bug report #1764174
+ {
+ "ween",
+ "",
+ {
+ {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
+ {"music__5.snd", 0, "7d1819b9981ecddd53d3aacbc75f1cc8", 13446},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "e6d13fb3b858cb4f78a8780d184d5b2c"),
+ FR_FRA,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "2bb8878a8042244dd2b96ff682381baa"),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "de92e5c6a8c163007ffceebef6e67f7d", 7117568),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by cybot_tmin in bug report #1667743
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "6d60f9205ecfbd8735da2ee7823a70dc", 7014426),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "4b10525a3782aa7ecd9d833b5c1d308b"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by cartman_ on #scummvm
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "63170e71f04faba88673b3f510f9c4c8"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by glorfindel in bugreport #1722142
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "8b57cd510da8a3bbd99e3a0297a8ebd1", 7018771),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "Demo",
+ AD_ENTRY1("intro.stk", "2e9c2898f6bf206ede801e3b2e7ee428"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, "show.tot", 0
+ },
+ {
+ {
+ "ween",
+ "Demo",
+ AD_ENTRY1("intro.stk", "15fb91a1b9b09684b28ac75edf66e504"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, "show.tot", 0
+ },
+ {
+ {
+ "bargon",
+ "",
+ AD_ENTRY1("intro.stk", "da3c54be18ab73fbdb32db24624a9c23"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by Trekky in the forums
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "2f54b330d21f65b04b7c1f8cca76426c", 262109),
+ FR_FRA,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by cesardark in bug #1681649
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "11103b304286c23945560b391fd37e7d", 3181890),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug #1692667
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by pwigren in bugreport #1764174
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "569d679fe41d49972d34c9fce5930dda", 269825),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by kizkoool in bugreport #2089734
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "00f6b4e2ee26e5c40b488e2df5adcf03", 3975580),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by glorfindel in bugreport #1722142
+ {
+ "bargon",
+ "Fanmade",
+ AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ {
+ {"intro.stk", 0, "0b72992f5d8b5e6e0330572a5753ea25", 256490},
+ {"mod.babayaga", 0, "43484cde74e0860785f8e19f0bc776d1", 60248},
+ {0, 0, 0, 0}
+ },
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "7b7f48490dedc8a7cb999388e2fadbe3", 3930674),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "e0767783ff662ed93665446665693aef", 4371238),
+ HE_ISR,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by cartman_ on #scummvm
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "f1f78b663893b58887add182a77df151", 3944090),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2105220
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "cd322cb3c64ef2ba2f2134aa2122cfe9", 3936700),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by koalet in bug report #2479034
+ {
+ "lit",
+ "",
+ {
+ {"intro.stk", 0, "af98bcdc70e1f1c1635577fd726fe7f1", 3937310},
+ {"musmac1.mid", 0, "ae7229bb09c6abe4e60a2768b24bc890", 9398},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2.6 Francais-Maths 4e
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "58ee9583a4fb837f02d9a58e5f442656", 3937120),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit1",
+ "Full install",
+ {
+ {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
+ {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 4396644},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit1",
+ "Light install",
+ {
+ {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
+ {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 664064},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit2",
+ "Light install",
+ AD_ENTRY1s("intro.stk", "17acbb212e62addbe48dc8f2282c98cb", 72318),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit2",
+ "Full install",
+ {
+ {"intro.stk", 0, "17acbb212e62addbe48dc8f2282c98cb", 72318},
+ {"partie4.itk", 0, "6ce4967e0c79d7daeabc6c1d26783d4c", 2612087},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "Demo",
+ AD_ENTRY1("demo.stk", "c06f8cc20eb239d4c71f225ce3093edf"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ "demo.stk", "demo.tot", 0
+ },
+ {
+ {
+ "lit",
+ "Non-interactive Demo",
+ AD_ENTRY1("demo.stk", "2eba8abd9e3878c57307576012dd2fec"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ "demo.stk", "demo.tot", 0
+ },
+ {
+ {
+ "fascination",
+ "CD Version (Censored)",
+ AD_ENTRY1s("disk0.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+ kGameTypeFascination,
+ kFeaturesCD,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("disk0.stk", "a50a8495e1b2d67699fb562cb98fc3e2", 1064387),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("intro.stk", "d6e45ce548598727e2b5587a99718eba", 1055909),
+ HE_ISR,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "intro.stk", 0, 0
+ },
+ { // Supplied by sanguine
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("disk0.stk", "c14330d052fe4da5a441ac9d81bc5891", 1061955),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ { // Supplied by windlepoons in bug report #2809247
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("disk0.stk", "3a24e60a035250189643c86a9ceafb97", 1062480),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "VGA",
+ AD_ENTRY1s("disk0.stk", "e8ab4f200a2304849f462dc901705599", 183337),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "68b1c01564f774c0b640075fbad1b695", 189968),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "7062117e9c5adfb6bfb2dac3ff74df9e", 189951),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "55c154e5a3e8e98afebdcff4b522e1eb", 190005),
+ FR_FRA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "7691827fff35df7799f14cfd6be178ad", 189931),
+ IT_ITA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "aff9fcc619f4dd19eae228affd0d34c8", 189964),
+ EN_ANY,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "geisha",
+ "",
+ AD_ENTRY1s("disk1.stk", "6eebbb98ad90cd3c44549fc2ab30f632", 212153),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGeisha,
+ kFeaturesNone,
+ "disk1.stk", "intro.tot", 0
+ },
+ {
+ {
+ "geisha",
+ "",
+ AD_ENTRY1s("disk1.stk", "f4d4d9d20f7ad1f879fc417d47faba89", 336732),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGeisha,
+ kFeaturesNone,
+ "disk1.stk", "intro.tot", 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "32b0f57f5ae79a9ae97e8011df38af42", 157084),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "904fc32032295baa3efb3a41f17db611", 178582),
+ HE_ISR,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by raziel_ in bug report #1891869
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "16b014bf32dbd6ab4c5163c44f56fed1", 445104),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by fac76 in bug report #1742716
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "32b0f57f5ae79a9ae97e8011df38af42", 157084},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "1e2f64ec8dfa89f42ee49936a27e66e7"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "f6d225b25a180606fa5dbe6405c97380"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "e42a4f2337d6549487a80864d7826972"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Paranoimia on #scummvm
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "fe8144daece35538085adb59c2d29613", 159402),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "4e3af248a48a2321364736afab868527"),
+ RU_RUS,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "8d28ce1591b0e9cc79bf41cad0fc4c9c"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2098621
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "d3b72938fbbc8159198088811f9e6d19", 160382),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
+ EN_GRB,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesNone,
+ 0, "menu.tot", 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesNone,
+ 0, "menu.tot", 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "edd7403e5dc2a14459d2665a4c17714d", 209534},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "edd7403e5dc2a14459d2665a4c17714d", 209534),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "6f2c226c62dd7ab0ab6f850e89d3fc47"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ HU_HUN,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "7aebd94e49c2c5c518c9e7b74f25de9d"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Interactive Demo 2",
+ AD_ENTRY1("intro.stk", "e5dcbc9f6658ebb1e8fe26bc4da0806d"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Interactive Demo 3",
+ AD_ENTRY1s("intro.stk", "9e20ad7b471b01f84db526da34eaf0a2", 395561),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Non-interactive Demo",
+ AD_ENTRY1("intro.stk", "b9b898fccebe02b69c086052d5024a55"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ EN_USA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "Non-Interactive Demo",
+ {
+ {"cons.imd", 0, "f896ba0c4a1ac7f7260d342655980b49", 17804},
+ {"conseil.imd", 0, "aaedd5482d5b271e233e86c5a03cf62e", 33999},
+ {"int.imd", 0, "6308222fcefbcb20925f01c1aff70dee", 30871},
+ {"inter.imd", 0, "39bd6d3540f3bedcc97293f352c7f3fc", 191719},
+ {"machu.imd", 0, "c0bc8211d93b467bfd063b63fe61b85c", 34609},
+ {"post.imd", 0, "d75cad0e3fc22cb0c8b6faf597f509b2", 1047709},
+ {"posta.imd", 0, "2a5b3fe75681ddf4d21ac724db8111b4", 547250},
+ {"postb.imd", 0, "24260ce4e80a4c472352b76637265d09", 868312},
+ {"postc.imd", 0, "24accbcc8b83a9c2be4bd82849a2bd29", 415637},
+ {"tum.imd", 0, "0993d4810ec9deb3f77c5e92095320fd", 20330},
+ {"tumi.imd", 0, "bf53f229480d694de0947fe3366fbec6", 248952},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib | kFeaturesBATDemo,
+ 0, 0, 7
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "5f5f4e0a72c33391e67a47674b120cc6", 20296422),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2098838
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "08a96bf061af1fa4f75c6a7cc56b60a4", 20734979),
+ PL_POL,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "Non-Interactive Demo",
+ {
+ {"demo.scn", 0, "16bb85fc5f8e519147b60475dbf33962", 89},
+ {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 1
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "6190e32404b672f4bbbc39cf76f41fda", 2511470),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "b3f8472484b7a1df94557b51e7b6fca0", 2322644),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "bdbdac8919200a5e71ffb9fb0709f704", 2446652),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "Demo",
+ AD_ENTRY1s("intro.stk", "464538a17ed39755d7f1ba9c751af1bd", 1847864),
+ EN_USA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "Demo",
+ AD_ENTRY1s("lda1.stk", "0e56a899357cbc0bf503260fd2dd634e", 15032774),
+ UNK_LANG,
+ kPlatformWindows,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ "lda1.stk", 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "Demo",
+ AD_ENTRY1s("lda1.stk", "8669ea2e9a8239c070dc73958fbc8753", 15567724),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ "lda1.stk", 0, 0
+ },
+ {
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "3ab2c542bd9216ae5d02cc6f45701ae1", 1252436),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by gamin in the forums
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "b991ed1d31c793e560edefdb349882ef", 1276408),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "4ec3c0864e2b54c5b4ccf9f6ad96528d", 1253328),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2770340
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "4bd31979ea3d77a58a358c09000a85ed", 1253018),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "urban",
+ "Non-Interactive Demo",
+ {
+ {"wdemo.s24", 0, "14ac9bd51db7a075d69ddb144904b271", 87},
+ {"demo.vmd", 0, "65d04715d871c292518b56dd160b0161", 9091237},
+ {"urband.vmd", 0, "60343891868c91854dd5c82766c70ecc", 922461},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeUrban,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 2
+ },
+ {
+ {
+ "playtoons1",
+ "",
+ {
+ {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
+ {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons1",
+ "Pack mes histoires anim\xE9""es",
+ {
+ {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
+ {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons1",
+ "",
+ {
+ {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
+ {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { // Supplied by scoriae in the forums
+ {
+ "playtoons1",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"archi.stk", 0, "00d8274519dfcf8a0d8ae3099daea0f8", 5532135},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"play123.scn", 0, "4689a31f543915e488c3bc46ea358add", 258},
+ {"archi.vmd", 0, "a410fcc8116bc173f038100f5857191c", 5617210},
+ {"chato.vmd", 0, "5a10e39cb66c396f2f9d8fb35e9ac016", 5445937},
+ {"genedeb.vmd", 0, "3bb4a45585f88f4d839efdda6a1b582b", 1244228},
+ {"generik.vmd", 0, "b46bdd64b063e86927fb2826500ad512", 603242},
+ {"genespi.vmd", 0, "b7611916f32a370ae9832962fc17ef72", 758719},
+ {"spirou.vmd", 0, "8513dbf7ac51c057b21d371d6b217b47", 2550788},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 3
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"e.scn", 0, "8a0db733c3f77be86e74e8242e5caa61", 124},
+ {"demarchg.vmd", 0, "d14a95da7d8792faf5503f649ffcbc12", 5619415},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 4
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"i.scn", 0, "8b3294474d39970463663edd22341730", 285},
+ {"demarita.vmd", 0, "84c8672b91c7312462603446e224bfec", 5742533},
+ {"dembouit.vmd", 0, "7a5fdf0a4dbdfe72e31dd489ea0f8aa2", 3536786},
+ {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
+ {"demoita.vmd", 0, "b4c0622d14c8749965cd0f5dfca4cf4b", 1183566},
+ {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 5
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"s.scn", 0, "1f527010626b5490761f16ba7a6f639a", 251},
+ {"demaresp.vmd", 0, "3f860f944056842b35a5fd05416f208e", 5720619},
+ {"demboues.vmd", 0, "3a0caa10c98ef92a15942f8274075b43", 3535838},
+ {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
+ {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
+ {0, 0, 0, 0}
+ },
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 6
+ },
+ {
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
+ {"spirou.stk", 0, "5d9c7644d0c47840169b4d016765cc1a", 9816201},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "55a85036dd93cce93532d8f743d90074", 17467154},
+ {"spirou.stk", 0, "e3e1b6148dd72fafc3637f1a8e5764f5", 9812043},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
+ {"spirou.stk", 0, "91080dc148de1bbd6a97321c1a1facf3", 9817086},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { // Supplied by scoriae in the forums
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"spirou.stk", 0, "993737f112ca6a9b33c814273280d832", 9825760},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
+ {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"chato.stk", 0, "8fc8d0da5b3e758908d1d7298d497d0b", 6041026},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "Pack mes histoires anim\xE9""es",
+ {
+ {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
+ {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
+ {"chato.stk", 0, "3c6cb3ac8a5a7cf681a19971a92a748d", 6033791},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
+ {"chato.stk", 0, "bdef407387112bfcee90e664865ac3af", 6033867},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons4",
+ "",
+ {
+ {"playtoon.stk", 0, "b7f5afa2dc1b0f75970b7c07d175db1b", 24340406},
+ {"manda.stk", 0, "92529e0b927191d9898a34c2892e9a3a", 6485072},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { //Supplied by goodoldgeorg in bug report #2820006
+ {
+ "playtoons4",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"manda.stk", 0, "69a79c9f61b2618e482726f2ff68078d", 6499208},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons5",
+ "",
+ {
+ {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
+ {"wakan.stk", 0, "f493bf82851bc5ba74d57de6b7e88df8", 5520153},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "bambou",
+ "",
+ {
+ {"intro.stk", 0, "2f8db6963ff8d72a8331627ebda918f4", 3613238},
+ {"bambou.itk", 0, "0875914d31126d0749313428f10c7768", 114440192},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBambou,
+ kFeatures640,
+ "intro.stk", "intro.tot", 0
+ },
+ {
+ {
+ "playtnck1",
+ "",
+ {
+ {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
+ {"dan.itk", 0, "906d67b3e438d5e95ec7ea9e781a94f3", 3000320},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtnck2",
+ "",
+ {
+ {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
+ {"dan.itk", 0, "74eeb075bd2cb47b243349730264af01", 3213312},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtnck3",
+ "",
+ {
+ {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
+ {"dan.itk", 0, "9a8f62809eca5a52f429b5b6a8e70f8f", 2861056},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.0 for Teachers",
+ AD_ENTRY1s("adi2.stk", "da6f1fb68bff32260c5eecdf9286a2f5", 1533168),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeaturesNone,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1. Exact version not specified.
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "23f279615c736dc38320f1348e70c36e", 10817668),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ { // Found in french ADI 2 Francais-Maths CE2. Exact version not specified.
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "d4162c4298f9423ecc1fb04965557e90", 11531214),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "29694c5a649298a42f87ae731d6d6f6d", 311132),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeaturesNone,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "2a40bb48ccbd4e6fb3f7f0fc2f069d80", 17720132),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.5",
+ AD_ENTRY1s("adi2.stk", "fcac60e6627f37aee219575b60859de9", 16944268),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.5",
+ AD_ENTRY1s("adi2.stk", "072d5e2d7826a7c055865568ebf918bb", 16934596),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.6",
+ AD_ENTRY1s("adi2.stk", "2fb940eb8105b12871f6b88c8c4d1615", 16780058),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.6",
+ AD_ENTRY1s("adi2.stk", "fde7d98a67dbf859423b6473796e932a", 18044780),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.7.1",
+ AD_ENTRY1s("adi2.stk", "6fa5dffebf5c7243c6af6b8c188ee00a", 19278008),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Non-Interactive Demo",
+ {
+ {"demo.scn", 0, "8b5ba359fd87d586ad39c1754bf6ea35", 168},
+ {"demadi2t.vmd", 0, "08a1b18cfe2015d3b43270da35cc813d", 7250723},
+ {"demarch.vmd", 0, "4c4a4616585d40ef3df209e3c3911062", 5622731},
+ {"demobou.vmd", 0, "2208b9855775564d15c4a5a559da0aec", 3550511},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdi2,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 1
+ },
+ {
+ {
+ "adi4",
+ "Addy 4 Grundschule Basis CD",
+ AD_ENTRY1s("intro.stk", "d2f0fb8909e396328dc85c0e29131ba8", 5847588),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Addy 4 Sekundarstufe Basis CD",
+ AD_ENTRY1s("intro.stk", "367340e59c461b4fa36651cd74e32c4e", 5847378),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0",
+ AD_ENTRY1s("intro.stk", "a3c35d19b2d28ea261d96321d208cb5a", 6021466),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0",
+ AD_ENTRY1s("intro.stk", "44491d85648810bc6fcf84f9b3aa47d5", 5834944),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0",
+ AD_ENTRY1s("intro.stk", "29374c0e3c10b17dd8463b06a55ad093", 6012072),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0 Limited Edition",
+ AD_ENTRY1s("intro.stk", "ebbbc5e28a4adb695535ed989c1b8d66", 5929644),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.10",
+ AD_ENTRY1s("intro.stk", "3e3fa9656e37d802027635ace88c4cc5", 5359144),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.10",
+ AD_ENTRY1s("intro.stk", "6afc2590856433b9f5295b032f2b205d", 5923112),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.11",
+ AD_ENTRY1s("intro.stk", "6296e4be4e0c270c24d1330881900c7f", 5921234),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Addy 4.21",
+ AD_ENTRY1s("intro.stk", "534f0b674cd4830df94a9c32c4ea7225", 6878034),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.21",
+ AD_ENTRY1s("intro.stk", "c5b9f6222c0b463f51dab47317c5b687", 5950490),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0 Interactive Demo",
+ AD_ENTRY1s("intro.stk", "89ace204dbaac001425c73f394334f6f", 2413102),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0 / Adibou 2 Demo",
+ AD_ENTRY1s("intro.stk", "d41d8cd98f00b204e9800998ecf8427e", 0),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "ajworld",
+ "",
+ AD_ENTRY1s("intro.stk", "e453bea7b28a67c930764d945f64d898", 3913628),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou1",
+ "ADIBOU 1 Environnement 4-7 ans",
+ AD_ENTRY1s("intro.stk", "6db110188fcb7c5208d9721b5282682a", 4805104),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdibou1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2",
+ AD_ENTRY1s("intro.stk", "94ae7004348dc8bf99c23a9a6ef81827", 956162),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "Le Jardin Magique d'Adibou",
+ AD_ENTRY1s("intro.stk", "a8ff86f3cc40dfe5898e0a741217ef27", 956328),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2",
+ AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIB\xD9 2",
+ AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU Version Decouverte",
+ AD_ENTRY1s("intro.stk", "558c14327b79ed39214b49d567a75e33", 8737856),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.10 Environnement",
+ AD_ENTRY1s("intro.stk", "f2b797819aeedee557e904b0b5ccd82e", 8736454),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.11 Environnement",
+ AD_ENTRY1s("intro.stk", "7b1f1f6f6477f54401e95d913f75e333", 8736904),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.12 Environnement",
+ AD_ENTRY1s("intro.stk", "1e49c39a4a3ce6032a84b712539c2d63", 8738134),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.13s Environnement",
+ AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOO 2.14 Environnement",
+ AD_ENTRY1s("intro.stk", "ff63637e3cb7f0a457edf79457b1c6b3", 9333874),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { AD_TABLE_END_MARKER, kGameTypeNone, kFeaturesNone, 0, 0, 0}
+};
+
+static const GOBGameDescription fallbackDescs[] = {
+ { //0
+ {
+ "gob1",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { //1
+ {
+ "gob1cd",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //2
+ {
+ "gob2",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //3
+ {
+ "gob2mac",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //4
+ {
+ "gob2cd",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //5
+ {
+ "bargon",
+ "",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { //6
+ {
+ "gob3",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //7
+ {
+ "gob3cd",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //8
+ {
+ "woodruff",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //9
+ {
+ "lostintime",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //10
+ {
+ "lostintime",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //11
+ {
+ "lostintime",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //12
+ {
+ "urban",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //13
+ {
+ "playtoons1",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //14
+ {
+ "playtoons2",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //15
+ {
+ "playtoons3",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //16
+ {
+ "playtoons4",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //17
+ {
+ "playtoons5",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //18
+ {
+ "playtoons construction kit",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //19
+ {
+ "bambou",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBambou,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //20
+ {
+ "fascination",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ { //21
+ {
+ "geisha",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGeisha,
+ kFeaturesNone,
+ "disk1.stk", "intro.tot", 0
+ },
+ { //22
+ {
+ "adi2",
+ "",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", 0, 0
+ },
+ { //23
+ {
+ "adi4",
+ "",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ "adif41.stk", 0, 0
+ },
+ { //24
+ {
+ "coktelplayer",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeUrban,
+ kFeaturesAdLib | kFeatures640 | kFeaturesSCNDemo,
+ "", "", 8
+ }
+};
+
+static const ADFileBasedFallback fileBased[] = {
+ { &fallbackDescs[ 0], { "intro.stk", "disk1.stk", "disk2.stk", "disk3.stk", "disk4.stk", 0 } },
+ { &fallbackDescs[ 1], { "intro.stk", "gob.lic", 0 } },
+ { &fallbackDescs[ 2], { "intro.stk", 0 } },
+ { &fallbackDescs[ 2], { "intro.stk", "disk2.stk", "disk3.stk", 0 } },
+ { &fallbackDescs[ 3], { "intro.stk", "disk2.stk", "disk3.stk", "musmac1.mid", 0 } },
+ { &fallbackDescs[ 4], { "intro.stk", "gobnew.lic", 0 } },
+ { &fallbackDescs[ 5], { "intro.stk", "scaa.imd", "scba.imd", "scbf.imd", 0 } },
+ { &fallbackDescs[ 6], { "intro.stk", "imd.itk", 0 } },
+ { &fallbackDescs[ 7], { "intro.stk", "mus_gob3.lic", 0 } },
+ { &fallbackDescs[ 8], { "intro.stk", "woodruff.itk", 0 } },
+ { &fallbackDescs[ 9], { "intro.stk", "commun1.itk", 0 } },
+ { &fallbackDescs[10], { "intro.stk", "commun1.itk", "musmac1.mid", 0 } },
+ { &fallbackDescs[11], { "intro.stk", "commun1.itk", "lost.lic", 0 } },
+ { &fallbackDescs[12], { "intro.stk", "cd1.itk", "objet1.itk", 0 } },
+ { &fallbackDescs[13], { "playtoon.stk", "archi.stk", 0 } },
+ { &fallbackDescs[14], { "playtoon.stk", "spirou.stk", 0 } },
+ { &fallbackDescs[15], { "playtoon.stk", "chato.stk", 0 } },
+ { &fallbackDescs[16], { "playtoon.stk", "manda.stk", 0 } },
+ { &fallbackDescs[17], { "playtoon.stk", "wakan.stk", 0 } },
+ { &fallbackDescs[18], { "playtoon.stk", "dan.itk" } },
+ { &fallbackDescs[19], { "intro.stk", "bambou.itk", 0 } },
+ { &fallbackDescs[20], { "disk0.stk", "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
+ { &fallbackDescs[21], { "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
+ { &fallbackDescs[22], { "adi2.stk", 0 } },
+ { &fallbackDescs[23], { "adif41.stk", "adim41.stk", 0 } },
+ { &fallbackDescs[24], { "coktelplayer.scn", 0 } },
+ { 0, { 0 } }
+};
+
+}
diff --git a/engines/gob/draw.cpp b/engines/gob/draw.cpp
index ff6d558998..36b75dc2eb 100644
--- a/engines/gob/draw.cpp
+++ b/engines/gob/draw.cpp
@@ -914,7 +914,7 @@ void Draw::winDraw(int16 fct) {
int table[10];
SurfaceDescPtr tempSrf;
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
if (_vm->_global->_curWinId) {
if (_fascinWin[_vm->_global->_curWinId].id == -1)
@@ -1032,7 +1032,7 @@ void Draw::winDraw(int16 fct) {
table[_fascinWin[i].id] = i;
}
- if ((_sourceSurface == 21) && (fct == 0)) {
+ if ((_sourceSurface == kBackSurface) && (fct == 0)) {
_vm->_video->drawSprite(*_spritesArray[_sourceSurface], *_spritesArray[_destSurface],
_spriteLeft, _spriteTop, _spriteLeft + _spriteRight - 1,
_spriteTop + _spriteBottom - 1, _destSpriteX, _destSpriteY, _transparency);
@@ -1267,11 +1267,11 @@ void Draw::winDraw(int16 fct) {
}
if (_renderFlags & 16) {
- if (_sourceSurface == 21) {
+ if (_sourceSurface == kBackSurface) {
_spriteLeft -= _backDeltaX;
_spriteTop -= _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX -= _backDeltaX;
_destSpriteY -= _backDeltaY;
}
@@ -1323,9 +1323,9 @@ void Draw::forceBlit(bool backwards) {
return;
if (_frontSurface == _backSurface)
return;
- if (_spritesArray[20] != _frontSurface)
+ if (_spritesArray[kFrontSurface] != _frontSurface)
return;
- if (_spritesArray[21] != _backSurface)
+ if (_spritesArray[kBackSurface] != _backSurface)
return;
if (!backwards) {
diff --git a/engines/gob/draw.h b/engines/gob/draw.h
index 8997c53362..fa3cbb84cc 100644
--- a/engines/gob/draw.h
+++ b/engines/gob/draw.h
@@ -45,7 +45,12 @@ namespace Gob {
class Draw {
public:
- static const int kFontCount = 8;
+ static const int kFontCount = 8;
+ static const int kFrontSurface = 20;
+ static const int kBackSurface = 21;
+ static const int kAnimSurface = 22;
+ static const int kCursorSurface = 23;
+ static const int kCaptureSurface = 30;
struct FontToSprite {
int8 sprite;
diff --git a/engines/gob/draw_fascin.cpp b/engines/gob/draw_fascin.cpp
index 9d30faa972..1e01db7dfb 100644
--- a/engines/gob/draw_fascin.cpp
+++ b/engines/gob/draw_fascin.cpp
@@ -53,12 +53,12 @@ void Draw_Fascination::spriteOperation(int16 operation) {
_destSurface -= 80;
if ((_renderFlags & RENDERFLAG_USEDELTAS) && !deltaVeto) {
- if ((_sourceSurface == 21) && (operation != DRAW_LOADSPRITE)) {
+ if ((_sourceSurface == kBackSurface) && (operation != DRAW_LOADSPRITE)) {
_spriteLeft += _backDeltaX;
_spriteTop += _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX += _backDeltaX;
_destSpriteY += _backDeltaY;
if ((operation == DRAW_DRAWLINE) ||
@@ -70,7 +70,7 @@ void Draw_Fascination::spriteOperation(int16 operation) {
}
if (_renderFlags & 0x20) {
- if (_destSurface == 21 || (operation == 0 && _sourceSurface == 21)) {
+ if (_destSurface == kBackSurface || (operation == 0 && _sourceSurface == kBackSurface)) {
winDraw(operation);
return;
}
@@ -86,7 +86,7 @@ void Draw_Fascination::spriteOperation(int16 operation) {
int16 destSurface = _destSurface;
int16 sourceSurface = _sourceSurface;
- if (_vm->_video->_splitSurf && ((_destSurface == 20) || (_destSurface == 21))) {
+ if (_vm->_video->_splitSurf && ((_destSurface == kFrontSurface) || (_destSurface == kBackSurface))) {
if ((_destSpriteY >= _vm->_video->_splitStart)) {
_destSpriteY -= _vm->_video->_splitStart;
if ((operation == DRAW_DRAWLINE) ||
@@ -340,12 +340,12 @@ void Draw_Fascination::spriteOperation(int16 operation) {
}
if ((_renderFlags & RENDERFLAG_USEDELTAS) && !deltaVeto) {
- if (_sourceSurface == 21) {
+ if (_sourceSurface == kBackSurface) {
_spriteLeft -= _backDeltaX;
_spriteTop -= _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX -= _backDeltaX;
_destSpriteY -= _backDeltaY;
}
diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp
index 862cdd33eb..583d13986e 100644
--- a/engines/gob/draw_playtoons.cpp
+++ b/engines/gob/draw_playtoons.cpp
@@ -53,12 +53,12 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
_destSurface -= 80;
if ((_renderFlags & RENDERFLAG_USEDELTAS) && !deltaVeto) {
- if ((_sourceSurface == 21) && (operation != DRAW_LOADSPRITE)) {
+ if ((_sourceSurface == kBackSurface) && (operation != DRAW_LOADSPRITE)) {
_spriteLeft += _backDeltaX;
_spriteTop += _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX += _backDeltaX;
_destSpriteY += _backDeltaY;
if ((operation == DRAW_DRAWLINE) ||
@@ -78,7 +78,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
int16 destSurface = _destSurface;
int16 sourceSurface = _sourceSurface;
- if (_vm->_video->_splitSurf && ((_destSurface == 20) || (_destSurface == 21))) {
+ if (_vm->_video->_splitSurf && ((_destSurface == kFrontSurface) || (_destSurface == kBackSurface))) {
if ((_destSpriteY >= _vm->_video->_splitStart)) {
_destSpriteY -= _vm->_video->_splitStart;
if ((operation == DRAW_DRAWLINE) ||
@@ -409,12 +409,12 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
}
if ((_renderFlags & RENDERFLAG_USEDELTAS) && !deltaVeto) {
- if (_sourceSurface == 21) {
+ if (_sourceSurface == kBackSurface) {
_spriteLeft -= _backDeltaX;
_spriteTop -= _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX -= _backDeltaX;
_destSpriteY -= _backDeltaY;
}
diff --git a/engines/gob/draw_v1.cpp b/engines/gob/draw_v1.cpp
index 719945fd6f..1cec15ce04 100644
--- a/engines/gob/draw_v1.cpp
+++ b/engines/gob/draw_v1.cpp
@@ -184,7 +184,7 @@ void Draw_v1::printTotText(int16 id) {
_destSpriteY = destY;
_spriteRight = spriteRight;
_spriteBottom = spriteBottom;
- _destSurface = 21;
+ _destSurface = kBackSurface;
_backColor = *ptr++;
_transparency = 1;
@@ -326,12 +326,12 @@ void Draw_v1::spriteOperation(int16 operation) {
_destSurface -= 80;
if (_renderFlags & RENDERFLAG_USEDELTAS) {
- if (_sourceSurface == 21) {
+ if (_sourceSurface == kBackSurface) {
_spriteLeft += _backDeltaX;
_spriteTop += _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX += _backDeltaX;
_destSpriteY += _backDeltaY;
if ((operation == DRAW_DRAWLINE) ||
@@ -508,12 +508,12 @@ void Draw_v1::spriteOperation(int16 operation) {
}
if (_renderFlags & RENDERFLAG_USEDELTAS) {
- if (_sourceSurface == 21) {
+ if (_sourceSurface == kBackSurface) {
_spriteLeft -= _backDeltaX;
_spriteTop -= _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX -= _backDeltaX;
_destSpriteY -= _backDeltaY;
}
diff --git a/engines/gob/draw_v2.cpp b/engines/gob/draw_v2.cpp
index 985f84aaef..5d001f4b59 100644
--- a/engines/gob/draw_v2.cpp
+++ b/engines/gob/draw_v2.cpp
@@ -51,30 +51,30 @@ void Draw_v2::initScreen() {
_scrollOffsetX = 0;
_scrollOffsetY = 0;
- initSpriteSurf(21, _vm->_video->_surfWidth, _vm->_video->_surfHeight, 0);
- _backSurface = _spritesArray[21];
+ initSpriteSurf(kBackSurface, _vm->_video->_surfWidth, _vm->_video->_surfHeight, 0);
+ _backSurface = _spritesArray[kBackSurface];
_vm->_video->clearSurf(*_backSurface);
- if (!_spritesArray[23]) {
- initSpriteSurf(23, 32, 16, 2);
- _cursorSpritesBack = _spritesArray[23];
+ if (!_spritesArray[kCursorSurface]) {
+ initSpriteSurf(kCursorSurface, 32, 16, 2);
+ _cursorSpritesBack = _spritesArray[kCursorSurface];
_cursorSprites = _cursorSpritesBack;
_scummvmCursor =
_vm->_video->initSurfDesc(_vm->_global->_videoMode, 16, 16, SCUMMVM_CURSOR);
}
- _spritesArray[20] = _frontSurface;
- _spritesArray[21] = _backSurface;
+ _spritesArray[kFrontSurface] = _frontSurface;
+ _spritesArray[kBackSurface ] = _backSurface;
_vm->_video->dirtyRectsAll();
}
void Draw_v2::closeScreen() {
- //freeSprite(23);
+ //freeSprite(kCursorSurface);
//_cursorSprites = 0;
//_cursorSpritesBack = 0;
//_scummvmCursor = 0;
- freeSprite(21);
+ freeSprite(kBackSurface);
}
void Draw_v2::blitCursor() {
@@ -273,7 +273,7 @@ void Draw_v2::printTotText(int16 id) {
_destSpriteY = destY;
_spriteRight = spriteRight;
_spriteBottom = spriteBottom;
- _destSurface = 21;
+ _destSurface = kBackSurface;
_backColor = *ptr++;
_transparency = 1;
@@ -629,12 +629,12 @@ void Draw_v2::spriteOperation(int16 operation) {
_destSurface -= 80;
if ((_renderFlags & RENDERFLAG_USEDELTAS) && !deltaVeto) {
- if ((_sourceSurface == 21) && (operation != DRAW_LOADSPRITE)) {
+ if ((_sourceSurface == kBackSurface) && (operation != DRAW_LOADSPRITE)) {
_spriteLeft += _backDeltaX;
_spriteTop += _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX += _backDeltaX;
_destSpriteY += _backDeltaY;
if ((operation == DRAW_DRAWLINE) ||
@@ -654,7 +654,7 @@ void Draw_v2::spriteOperation(int16 operation) {
int16 destSurface = _destSurface;
int16 sourceSurface = _sourceSurface;
- if (_vm->_video->_splitSurf && ((_destSurface == 20) || (_destSurface == 21))) {
+ if (_vm->_video->_splitSurf && ((_destSurface == kFrontSurface) || (_destSurface == kBackSurface))) {
if ((_destSpriteY >= _vm->_video->_splitStart)) {
_destSpriteY -= _vm->_video->_splitStart;
if ((operation == DRAW_DRAWLINE) ||
@@ -908,12 +908,12 @@ void Draw_v2::spriteOperation(int16 operation) {
}
if ((_renderFlags & RENDERFLAG_USEDELTAS) && !deltaVeto) {
- if (_sourceSurface == 21) {
+ if (_sourceSurface == kBackSurface) {
_spriteLeft -= _backDeltaX;
_spriteTop -= _backDeltaY;
}
- if (_destSurface == 21) {
+ if (_destSurface == kBackSurface) {
_destSpriteX -= _backDeltaX;
_destSpriteY -= _backDeltaY;
}
diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp
index ddf095a5d1..1a8823b156 100644
--- a/engines/gob/game.cpp
+++ b/engines/gob/game.cpp
@@ -286,8 +286,8 @@ void Game::playTot(int16 skipPlay) {
_vm->_mult->initAll();
_vm->_mult->zeroMultData();
- _vm->_draw->_spritesArray[20] = _vm->_draw->_frontSurface;
- _vm->_draw->_spritesArray[21] = _vm->_draw->_backSurface;
+ _vm->_draw->_spritesArray[Draw::kFrontSurface] = _vm->_draw->_frontSurface;
+ _vm->_draw->_spritesArray[Draw::kBackSurface ] = _vm->_draw->_backSurface;
_vm->_draw->_cursorSpritesBack = _vm->_draw->_cursorSprites;
} else
_vm->_inter->initControlVars(0);
@@ -299,7 +299,7 @@ void Game::playTot(int16 skipPlay) {
break;
if (skipPlay == -2) {
- _vm->_vidPlayer->primaryClose();
+ _vm->_vidPlayer->closeVideo();
skipPlay = 0;
}
@@ -397,10 +397,10 @@ void Game::capturePush(int16 left, int16 top, int16 width, int16 height) {
left &= 0xFFF0;
right |= 0xF;
- _vm->_draw->initSpriteSurf(30 + _captureCount, right - left + 1, height, 0);
+ _vm->_draw->initSpriteSurf(Draw::kCaptureSurface + _captureCount, right - left + 1, height, 0);
- _vm->_draw->_sourceSurface = 21;
- _vm->_draw->_destSurface = 30 + _captureCount;
+ _vm->_draw->_sourceSurface = Draw::kBackSurface;
+ _vm->_draw->_destSurface = Draw::kCaptureSurface + _captureCount;
_vm->_draw->_spriteLeft = left;
_vm->_draw->_spriteRight = right - left + 1;
@@ -425,13 +425,13 @@ void Game::capturePop(char doDraw) {
_captureStack[_captureCount].height();
_vm->_draw->_transparency = 0;
- _vm->_draw->_sourceSurface = 30 + _captureCount;
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_sourceSurface = Draw::kCaptureSurface + _captureCount;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_spriteLeft = _vm->_draw->_destSpriteX & 0xF;
_vm->_draw->_spriteTop = 0;
_vm->_draw->spriteOperation(0);
}
- _vm->_draw->freeSprite(30 + _captureCount);
+ _vm->_draw->freeSprite(Draw::kCaptureSurface + _captureCount);
}
void Game::freeSoundSlot(int16 slot) {
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
index e3472e9fe1..f904c8c802 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -126,8 +126,13 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst) {
_pauseStart = 0;
// Setup mixer
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ bool muteSFX = ConfMan.getBool("mute") || ConfMan.getBool("sfx_mute");
+ bool muteMusic = ConfMan.getBool("mute") || ConfMan.getBool("music_mute");
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType,
+ muteSFX ? 0 : ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType,
+ muteMusic ? 0 : ConfMan.getInt("music_volume"));
_copyProtection = ConfMan.getBool("copy_protection");
@@ -345,8 +350,8 @@ void GobEngine::pauseGame() {
}
bool GobEngine::initGameParts() {
- _noMusic = MidiDriver::parseMusicDriver(ConfMan.get("music_driver")) == MD_NULL;
-
+ // just detect some devices some of which will be always there if the music is not disabled
+ _noMusic = MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB)) == MT_NULL ? true : false;
_saveLoad = 0;
_global = new Global(this);
diff --git a/engines/gob/hotspots.cpp b/engines/gob/hotspots.cpp
index abdf513393..396d9f0a22 100644
--- a/engines/gob/hotspots.cpp
+++ b/engines/gob/hotspots.cpp
@@ -2082,7 +2082,7 @@ void Hotspots::getTextCursorPos(const Font &font, const char *str,
}
void Hotspots::fillRect(uint16 x, uint16 y, uint16 width, uint16 height, uint16 color) const {
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_destSpriteX = x;
_vm->_draw->_destSpriteY = y;
_vm->_draw->_spriteRight = width;
diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp
index 24a8e0a390..3da71a2ba6 100644
--- a/engines/gob/init.cpp
+++ b/engines/gob/init.cpp
@@ -174,9 +174,11 @@ void Init::initGame() {
_vm->_util->longDelay(200); // Letting everything settle
- if (_vm->_vidPlayer->primaryOpen("coktel.imd")) {
- _vm->_vidPlayer->primaryPlay();
- _vm->_vidPlayer->primaryClose();
+ VideoPlayer::Properties props;
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "coktel.imd", props)) >= 0) {
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
_vm->_draw->closeScreen();
diff --git a/engines/gob/inter_bargon.cpp b/engines/gob/inter_bargon.cpp
index da8ca103aa..5c56196641 100644
--- a/engines/gob/inter_bargon.cpp
+++ b/engines/gob/inter_bargon.cpp
@@ -72,17 +72,47 @@ void Inter_Bargon::setupOpcodesGob() {
}
void Inter_Bargon::oBargon_intro0(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scaa", 0, 160)) {
- _vm->_vidPlayer->primaryPlay(0, 92, 27, 0, 0, 0);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 0;
+ props.y = 160;
+ props.startFrame = 0;
+ props.lastFrame = 92;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scaa", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_Bargon::oBargon_intro1(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scaa", 0, 160)) {
- _vm->_vidPlayer->primaryPlay(0, -1, 27, 0, 0, 0, 0, 0, true, 23);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 0;
+ props.y = 160;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+ props.fade = true;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scaa", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+
+ props.startFrame = -1;
+ props.lastFrame = _vm->_vidPlayer->getFrameCount(slot) - 23;
+ props.fade = false;
+
+ _vm->_vidPlayer->play(slot, props);
+
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_Bargon::oBargon_intro2(OpGobParams &params) {
@@ -178,45 +208,106 @@ void Inter_Bargon::oBargon_intro3(OpGobParams &params) {
}
void Inter_Bargon::oBargon_intro4(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scba", 191, 54)) {
- _vm->_vidPlayer->primaryPlay(0, -1, 27, 0, 0, 0, 0, 0, true);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 191;
+ props.y = 54;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+ props.fade = true;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scba", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_Bargon::oBargon_intro5(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scbb", 191, 54)) {
- _vm->_vidPlayer->primaryPlay(0, -1, 27, 0, 0, 0);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 191;
+ props.y = 54;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scbb", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_Bargon::oBargon_intro6(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scbc", 191, 54)) {
- _vm->_vidPlayer->primaryPlay(0, -1, 27, 0, 0, 0);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 191;
+ props.y = 54;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scbc", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_Bargon::oBargon_intro7(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scbf", 191, 54)) {
- _vm->_vidPlayer->primaryPlay(0, -1, 27, 0, 0, 0);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 191;
+ props.y = 54;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scbf", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_Bargon::oBargon_intro8(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scbc", 191, 54)) {
- _vm->_vidPlayer->primaryPlay(0, -1, 27, 0, 0, 0);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 191;
+ props.y = 54;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scbc", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_Bargon::oBargon_intro9(OpGobParams &params) {
- if (_vm->_vidPlayer->primaryOpen("scbd", 191, 54)) {
- _vm->_vidPlayer->primaryPlay(0, -1, 27, 0, 0, 0);
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties props;
+
+ props.x = 191;
+ props.y = 54;
+ props.palCmd = 0;
+ props.palStart = 0;
+ props.palEnd = 0;
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, "scbd", props)) < 0)
+ return;
+
+ _vm->_vidPlayer->play(slot, props);
+ _vm->_vidPlayer->closeVideo(slot);
}
} // End of namespace Gob
diff --git a/engines/gob/inter_fascin.cpp b/engines/gob/inter_fascin.cpp
index 5738197539..304f02f4fa 100644
--- a/engines/gob/inter_fascin.cpp
+++ b/engines/gob/inter_fascin.cpp
@@ -131,21 +131,41 @@ bool Inter_Fascination::oFascin_copySprite(OpFuncParams &params) {
void Inter_Fascination::oFascin_playTirb(OpGobParams &params) {
warning("funcPlayImd with parameter : 'tirb.imd'");
- if (_vm->_vidPlayer->primaryOpen("tirb", 150, 88, VideoPlayer::kFlagFrontSurface,
- VideoPlayer::kVideoTypePreIMD, 128, 80)) {
- _vm->_vidPlayer->primaryPlay();
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties vidProps;
+
+ vidProps.type = VideoPlayer::kVideoTypePreIMD;
+ vidProps.sprite = Draw::kFrontSurface;
+ vidProps.x = 150;
+ vidProps.y = 88;
+ vidProps.width = 128;
+ vidProps.height = 80;
+
+ int vidSlot = _vm->_vidPlayer->openVideo(true, "tirb", vidProps);
+ if (vidSlot < 0)
+ return;
+
+ _vm->_vidPlayer->play(vidSlot, vidProps);
+ _vm->_vidPlayer->closeVideo(vidSlot);
}
void Inter_Fascination::oFascin_playTira(OpGobParams &params) {
warning("funcPlayImd with parameter : 'tira.imd'");
- if (_vm->_vidPlayer->primaryOpen("tira", 88, 66, VideoPlayer::kFlagFrontSurface,
- VideoPlayer::kVideoTypePreIMD, 128, 80)) {
- _vm->_vidPlayer->primaryPlay();
- _vm->_vidPlayer->primaryClose();
- }
+ VideoPlayer::Properties vidProps;
+
+ vidProps.type = VideoPlayer::kVideoTypePreIMD;
+ vidProps.sprite = Draw::kFrontSurface;
+ vidProps.x = 88;
+ vidProps.y = 66;
+ vidProps.width = 128;
+ vidProps.height = 80;
+
+ int vidSlot = _vm->_vidPlayer->openVideo(true, "tira", vidProps);
+ if (vidSlot < 0)
+ return;
+
+ _vm->_vidPlayer->play(vidSlot, vidProps);
+ _vm->_vidPlayer->closeVideo(vidSlot);
}
void Inter_Fascination::oFascin_loadExtasy(OpGobParams &params) {
diff --git a/engines/gob/inter_playtoons.cpp b/engines/gob/inter_playtoons.cpp
index c9b962579b..befed4b1c2 100644
--- a/engines/gob/inter_playtoons.cpp
+++ b/engines/gob/inter_playtoons.cpp
@@ -107,7 +107,7 @@ bool Inter_Playtoons::oPlaytoons_printText(OpFuncParams &params) {
_vm->_draw->_backColor = _vm->_game->_script->readValExpr();
_vm->_draw->_frontColor = _vm->_game->_script->readValExpr();
_vm->_draw->_fontIndex = _vm->_game->_script->readValExpr();
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_textToPrint = buf;
_vm->_draw->_transparency = 0;
@@ -362,7 +362,6 @@ void Inter_Playtoons::oPlaytoons_getObjAnimSize() {
int16 objIndex;
uint16 readVar[4];
uint8 i;
- bool break_fl;
Mult::Mult_AnimData animData;
_vm->_game->_script->evalExpr(&objIndex);
@@ -375,7 +374,6 @@ void Inter_Playtoons::oPlaytoons_getObjAnimSize() {
return;
}
if (objIndex == -2) {
- break_fl = false;
warning("oPlaytoons_getObjAnimSize case -2 not implemented");
return;
}
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index 805893d8a7..11fe0c9c5e 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -328,7 +328,7 @@ void Inter_v1::o1_initCursor() {
(height != _vm->_draw->_cursorHeight) ||
(_vm->_draw->_cursorSprites->getWidth() != (width * count))) {
- _vm->_draw->freeSprite(23);
+ _vm->_draw->freeSprite(Draw::kCursorSurface);
_vm->_draw->_cursorSprites.reset();
_vm->_draw->_cursorSpritesBack.reset();
_vm->_draw->_scummvmCursor.reset();
@@ -344,9 +344,9 @@ void Inter_v1::o1_initCursor() {
if (count > 0x80)
count -= 0x80;
- _vm->_draw->initSpriteSurf(23, _vm->_draw->_cursorWidth * count,
+ _vm->_draw->initSpriteSurf(Draw::kCursorSurface, _vm->_draw->_cursorWidth * count,
_vm->_draw->_cursorHeight, 2);
- _vm->_draw->_cursorSpritesBack = _vm->_draw->_spritesArray[23];
+ _vm->_draw->_cursorSpritesBack = _vm->_draw->_spritesArray[Draw::kCursorSurface];
_vm->_draw->_cursorSprites = _vm->_draw->_cursorSpritesBack;
_vm->_draw->_scummvmCursor =
@@ -482,14 +482,14 @@ void Inter_v1::o1_initMult() {
if (_vm->_mult->_animSurf &&
((oldAnimWidth != _vm->_mult->_animWidth) ||
(oldAnimHeight != _vm->_mult->_animHeight))) {
- _vm->_draw->freeSprite(22);
+ _vm->_draw->freeSprite(Draw::kAnimSurface);
_vm->_mult->_animSurf.reset();
}
if (!_vm->_mult->_animSurf) {
- _vm->_draw->initSpriteSurf(22, _vm->_mult->_animWidth,
+ _vm->_draw->initSpriteSurf(Draw::kAnimSurface, _vm->_mult->_animWidth,
_vm->_mult->_animHeight, 0);
- _vm->_mult->_animSurf = _vm->_draw->_spritesArray[22];
+ _vm->_mult->_animSurf = _vm->_draw->_spritesArray[Draw::kAnimSurface];
}
_vm->_video->drawSprite(*_vm->_draw->_backSurface, *_vm->_mult->_animSurf,
@@ -813,6 +813,14 @@ bool Inter_v1::o1_if(OpFuncParams &params) {
byte cmd;
bool boolRes;
+ // WORKAROUND: Windows Gob1 OUTODDV reload goblin stuck bug present in original
+ if ((_vm->getGameType() == kGameTypeGob1) && (_vm->_game->_script->pos() == 11294) &&
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt00.tot") && VAR(59) == 1) {
+ warning("Workaround for Win Gob1 OUTODDV Reload Goblin Stuck Bug...");
+ WRITE_VAR(285, 0);
+ WRITE_VAR(59, 0);
+ }
+
boolRes = _vm->_game->_script->evalBoolResult();
if (boolRes) {
if ((params.counter == params.cmdCount) && (params.retFlag == 2))
@@ -914,7 +922,7 @@ bool Inter_v1::o1_printText(OpFuncParams &params) {
_vm->_draw->_backColor = _vm->_game->_script->readValExpr();
_vm->_draw->_frontColor = _vm->_game->_script->readValExpr();
_vm->_draw->_fontIndex = _vm->_game->_script->readValExpr();
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_textToPrint = buf;
_vm->_draw->_transparency = 0;
@@ -1331,7 +1339,8 @@ bool Inter_v1::o1_goblinFunc(OpFuncParams &params) {
gobParams.retVarPtr.set(*_variables, 236);
cmd = _vm->_game->_script->readInt16();
- _vm->_game->_script->skip(2);
+ gobParams.paramCount = _vm->_game->_script->readInt16();
+
if ((cmd > 0) && (cmd < 17)) {
objDescSet = true;
gobParams.extraData = _vm->_game->_script->readInt16();
diff --git a/engines/gob/inter_v2.cpp b/engines/gob/inter_v2.cpp
index 72764eec8d..0003332e47 100644
--- a/engines/gob/inter_v2.cpp
+++ b/engines/gob/inter_v2.cpp
@@ -360,24 +360,24 @@ void Inter_v2::o2_initMult() {
if (_vm->_mult->_animSurf &&
((oldAnimWidth != _vm->_mult->_animWidth) ||
(oldAnimHeight != _vm->_mult->_animHeight))) {
- _vm->_draw->freeSprite(22);
+ _vm->_draw->freeSprite(Draw::kAnimSurface);
_vm->_mult->_animSurf.reset();
}
_vm->_draw->adjustCoords(0,
&_vm->_mult->_animWidth, &_vm->_mult->_animHeight);
if (!_vm->_mult->_animSurf) {
- _vm->_draw->initSpriteSurf(22, _vm->_mult->_animWidth,
+ _vm->_draw->initSpriteSurf(Draw::kAnimSurface, _vm->_mult->_animWidth,
_vm->_mult->_animHeight, 0);
- _vm->_mult->_animSurf = _vm->_draw->_spritesArray[22];
+ _vm->_mult->_animSurf = _vm->_draw->_spritesArray[Draw::kAnimSurface];
if (_terminate)
return;
}
_vm->_draw->adjustCoords(1,
&_vm->_mult->_animWidth, &_vm->_mult->_animHeight);
- _vm->_draw->_sourceSurface = 21;
- _vm->_draw->_destSurface = 22;
+ _vm->_draw->_sourceSurface = Draw::kBackSurface;
+ _vm->_draw->_destSurface = Draw::kAnimSurface;
_vm->_draw->_spriteLeft = _vm->_mult->_animLeft;
_vm->_draw->_spriteTop = _vm->_mult->_animTop;
_vm->_draw->_spriteRight = _vm->_mult->_animWidth;
@@ -481,7 +481,7 @@ void Inter_v2::o2_loadMultObject() {
if ((((int32) *(obj.pPosX)) == -1234) && (((int32) *(obj.pPosY)) == -4321)) {
if (obj.videoSlot > 0)
- _vm->_vidPlayer->slotClose(obj.videoSlot - 1);
+ _vm->_vidPlayer->closeVideo(obj.videoSlot - 1);
obj.videoSlot = 0;
obj.lastLeft = -1;
@@ -959,50 +959,50 @@ void Inter_v2::o2_setScrollOffset() {
void Inter_v2::o2_playImd() {
char imd[128];
- int16 x, y;
- int16 startFrame;
- int16 lastFrame;
- int16 breakKey;
- int16 flags;
- int16 palStart;
- int16 palEnd;
- uint16 palCmd;
bool close;
_vm->_game->_script->evalExpr(0);
_vm->_game->_script->getResultStr()[8] = 0;
strncpy0(imd, _vm->_game->_script->getResultStr(), 127);
- x = _vm->_game->_script->readValExpr();
- y = _vm->_game->_script->readValExpr();
- startFrame = _vm->_game->_script->readValExpr();
- lastFrame = _vm->_game->_script->readValExpr();
- breakKey = _vm->_game->_script->readValExpr();
- flags = _vm->_game->_script->readValExpr();
- palStart = _vm->_game->_script->readValExpr();
- palEnd = _vm->_game->_script->readValExpr();
- palCmd = 1 << (flags & 0x3F);
+ VideoPlayer::Properties props;
- debugC(1, kDebugVideo, "Playing video \"%s\" @ %d+%d, frames %d - %d, "
- "paletteCmd %d (%d - %d), flags %X", _vm->_game->_script->getResultStr(), x, y,
- startFrame, lastFrame, palCmd, palStart, palEnd, flags);
+ props.x = _vm->_game->_script->readValExpr();
+ props.y = _vm->_game->_script->readValExpr();
+ props.startFrame = _vm->_game->_script->readValExpr();
+ props.lastFrame = _vm->_game->_script->readValExpr();
+ props.breakKey = _vm->_game->_script->readValExpr();
+ props.flags = _vm->_game->_script->readValExpr();
+ props.palStart = _vm->_game->_script->readValExpr();
+ props.palEnd = _vm->_game->_script->readValExpr();
+ props.palCmd = 1 << (props.flags & 0x3F);
- if ((imd[0] != 0) && !_vm->_vidPlayer->primaryOpen(imd, x, y, flags)) {
- WRITE_VAR(11, (uint32) -1);
- return;
+ debugC(1, kDebugVideo, "Playing video \"%s\" @ %d+%d, frames %d - %d, "
+ "paletteCmd %d (%d - %d), flags %X", imd,
+ props.x, props.y, props.startFrame, props.lastFrame,
+ props.palCmd, props.palStart, props.palEnd, props.flags);
+
+ int slot = 0;
+ if (imd[0] != 0) {
+ _vm->_vidPlayer->evaluateFlags(props);
+ if ((slot = _vm->_vidPlayer->openVideo(true, imd, props)) < 0) {
+ WRITE_VAR(11, (uint32) -1);
+ return;
+ }
}
- close = (lastFrame == -1);
- if (startFrame == -2) {
- startFrame = lastFrame = 0;
+ close = (props.lastFrame == -1);
+ if (props.startFrame == -2) {
+ props.startFrame = 0;
+ props.lastFrame = 0;
close = false;
}
- if (startFrame >= 0)
- _vm->_vidPlayer->primaryPlay(startFrame, lastFrame, breakKey, palCmd, palStart, palEnd, 0);
+ if (props.startFrame >= 0)
+ _vm->_vidPlayer->play(slot, props);
if (close)
- _vm->_vidPlayer->primaryClose();
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_v2::o2_getImdInfo() {
@@ -1011,10 +1011,10 @@ void Inter_v2::o2_getImdInfo() {
int16 varWidth, varHeight;
_vm->_game->_script->evalExpr(0);
- varX = _vm->_game->_script->readVarIndex();
- varY = _vm->_game->_script->readVarIndex();
+ varX = _vm->_game->_script->readVarIndex();
+ varY = _vm->_game->_script->readVarIndex();
varFrames = _vm->_game->_script->readVarIndex();
- varWidth = _vm->_game->_script->readVarIndex();
+ varWidth = _vm->_game->_script->readVarIndex();
varHeight = _vm->_game->_script->readVarIndex();
// WORKAROUND: The nut rolling animation in the administration center
@@ -1106,7 +1106,7 @@ bool Inter_v2::o2_printText(OpFuncParams &params) {
_vm->_draw->_backColor = _vm->_game->_script->readValExpr();
_vm->_draw->_frontColor = _vm->_game->_script->readValExpr();
_vm->_draw->_fontIndex = _vm->_game->_script->readValExpr();
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_textToPrint = buf;
_vm->_draw->_transparency = 0;
diff --git a/engines/gob/inter_v3.cpp b/engines/gob/inter_v3.cpp
index beace1b7d8..10ed23619d 100644
--- a/engines/gob/inter_v3.cpp
+++ b/engines/gob/inter_v3.cpp
@@ -253,8 +253,8 @@ bool Inter_v3::o3_copySprite(OpFuncParams &params) {
o1_copySprite(params);
// For the close-up "fading" in the CD version
- if (_vm->_draw->_destSurface == 20)
- _vm->_video->sparseRetrace(20);
+ if (_vm->_draw->_destSurface == Draw::kFrontSurface)
+ _vm->_video->sparseRetrace(Draw::kFrontSurface);
return false;
}
diff --git a/engines/gob/inter_v4.cpp b/engines/gob/inter_v4.cpp
index 1f6899d85c..d0824ffb58 100644
--- a/engines/gob/inter_v4.cpp
+++ b/engines/gob/inter_v4.cpp
@@ -142,14 +142,6 @@ void Inter_v4::o4_initScreen() {
void Inter_v4::o4_playVmdOrMusic() {
char fileName[128];
- int16 x, y;
- int16 startFrame;
- int16 lastFrame;
- int16 breakKey;
- int16 flags;
- int16 palStart;
- int16 palEnd;
- uint16 palCmd;
bool close;
_vm->_game->_script->evalExpr(0);
@@ -161,83 +153,92 @@ void Inter_v4::o4_playVmdOrMusic() {
(!scumm_stricmp(fileName, "noixroule")))
strcpy(fileName, "noixroul");
- x = _vm->_game->_script->readValExpr();
- y = _vm->_game->_script->readValExpr();
- startFrame = _vm->_game->_script->readValExpr();
- lastFrame = _vm->_game->_script->readValExpr();
- breakKey = _vm->_game->_script->readValExpr();
- flags = _vm->_game->_script->readValExpr();
- palStart = _vm->_game->_script->readValExpr();
- palEnd = _vm->_game->_script->readValExpr();
- palCmd = 1 << (flags & 0x3F);
+ VideoPlayer::Properties props;
+
+ props.x = _vm->_game->_script->readValExpr();
+ props.y = _vm->_game->_script->readValExpr();
+ props.startFrame = _vm->_game->_script->readValExpr();
+ props.lastFrame = _vm->_game->_script->readValExpr();
+ props.breakKey = _vm->_game->_script->readValExpr();
+ props.flags = _vm->_game->_script->readValExpr();
+ props.palStart = _vm->_game->_script->readValExpr();
+ props.palEnd = _vm->_game->_script->readValExpr();
+ props.palCmd = 1 << (props.flags & 0x3F);
debugC(1, kDebugVideo, "Playing video \"%s\" @ %d+%d, frames %d - %d, "
- "paletteCmd %d (%d - %d), flags %X", fileName, x, y, startFrame, lastFrame,
- palCmd, palStart, palEnd, flags);
+ "paletteCmd %d (%d - %d), flags %X", fileName,
+ props.x, props.y, props.startFrame, props.lastFrame,
+ props.palCmd, props.palStart, props.palEnd, props.flags);
close = false;
- if (lastFrame == -1) {
+ if (props.lastFrame == -1) {
close = true;
- } else if (lastFrame == -2) {
- } else if (lastFrame == -3) {
+ } else if (props.lastFrame == -2) {
+ } else if (props.lastFrame == -3) {
+
+ props.flags = VideoPlayer::kFlagOtherSurface;
+ props.sprite = -1;
- _vm->_mult->_objects[startFrame].pAnimData->animation = -startFrame - 1;
+ _vm->_mult->_objects[props.startFrame].pAnimData->animation = -props.startFrame - 1;
- if (_vm->_mult->_objects[startFrame].videoSlot > 0)
- _vm->_vidPlayer->slotClose(_vm->_mult->_objects[startFrame].videoSlot - 1);
+ if (_vm->_mult->_objects[props.startFrame].videoSlot > 0)
+ _vm->_vidPlayer->closeVideo(_vm->_mult->_objects[props.startFrame].videoSlot - 1);
- int slot = _vm->_vidPlayer->slotOpen(fileName);
+ int slot = _vm->_vidPlayer->openVideo(false, fileName, props);
- _vm->_mult->_objects[startFrame].videoSlot = slot + 1;
+ _vm->_mult->_objects[props.startFrame].videoSlot = slot + 1;
- if (x == -1) {
- *_vm->_mult->_objects[startFrame].pPosX = _vm->_vidPlayer->getDefaultX(slot);
- *_vm->_mult->_objects[startFrame].pPosY = _vm->_vidPlayer->getDefaultY(slot);
+ if (props.x == -1) {
+ *_vm->_mult->_objects[props.startFrame].pPosX = _vm->_vidPlayer->getDefaultX(slot);
+ *_vm->_mult->_objects[props.startFrame].pPosY = _vm->_vidPlayer->getDefaultY(slot);
} else {
- *_vm->_mult->_objects[startFrame].pPosX = x;
- *_vm->_mult->_objects[startFrame].pPosY = y;
+ *_vm->_mult->_objects[props.startFrame].pPosX = props.x;
+ *_vm->_mult->_objects[props.startFrame].pPosY = props.y;
}
return;
- } else if (lastFrame == -4) {
+ } else if (props.lastFrame == -4) {
warning("Woodruff Stub: Video/Music command -4: Play background video %s", fileName);
return;
- } else if (lastFrame == -5) {
+ } else if (props.lastFrame == -5) {
_vm->_sound->bgStop();
return;
- } else if (lastFrame == -6) {
+ } else if (props.lastFrame == -6) {
return;
- } else if (lastFrame == -7) {
+ } else if (props.lastFrame == -7) {
return;
- } else if (lastFrame == -8) {
+ } else if (props.lastFrame == -8) {
warning("Woodruff Stub: Video/Music command -8: Play background video %s", fileName);
return;
- } else if (lastFrame == -9) {
+ } else if (props.lastFrame == -9) {
_vm->_sound->bgStop();
_vm->_sound->bgSetPlayMode(BackgroundAtmosphere::kPlayModeRandom);
- _vm->_sound->bgPlay(fileName, "SND", SOUND_SND, palStart);
+ _vm->_sound->bgPlay(fileName, "SND", SOUND_SND, props.palStart);
return;
- } else if (lastFrame < 0) {
- warning("Unknown Video/Music command: %d, %s", lastFrame, fileName);
+ } else if (props.lastFrame < 0) {
+ warning("Unknown Video/Music command: %d, %s", props.lastFrame, fileName);
return;
}
- if (startFrame == -2) {
- startFrame = 0;
- lastFrame = -1;
+ if (props.startFrame == -2) {
+ props.startFrame = 0;
+ props.lastFrame = -1;
close = false;
}
- if ((fileName[0] != 0) && !_vm->_vidPlayer->primaryOpen(fileName, x, y, flags)) {
+ _vm->_vidPlayer->evaluateFlags(props);
+
+ int slot;
+ if ((fileName[0] != 0) && ((slot = _vm->_vidPlayer->openVideo(true, fileName, props)) < 0)) {
WRITE_VAR(11, (uint32) -1);
return;
}
- if (startFrame >= 0)
- _vm->_vidPlayer->primaryPlay(startFrame, lastFrame, breakKey, palCmd, palStart, palEnd, 0);
+ if (props.startFrame >= 0)
+ _vm->_vidPlayer->play(slot, props);
if (close)
- _vm->_vidPlayer->primaryClose();
+ _vm->_vidPlayer->closeVideo(slot);
}
} // End of namespace Gob
diff --git a/engines/gob/inter_v6.cpp b/engines/gob/inter_v6.cpp
index cbc831b5a1..73ef46bf31 100644
--- a/engines/gob/inter_v6.cpp
+++ b/engines/gob/inter_v6.cpp
@@ -102,88 +102,86 @@ void Inter_v6::o6_totSub() {
void Inter_v6::o6_playVmdOrMusic() {
char fileName[128];
- int16 x, y;
- int16 startFrame;
- int16 lastFrame;
- int16 breakKey;
- int16 flags;
- int16 palStart;
- int16 palEnd;
- uint16 palCmd;
bool close;
_vm->_game->_script->evalExpr(0);
strncpy0(fileName, _vm->_game->_script->getResultStr(), 127);
- x = _vm->_game->_script->readValExpr();
- y = _vm->_game->_script->readValExpr();
- startFrame = _vm->_game->_script->readValExpr();
- lastFrame = _vm->_game->_script->readValExpr();
- breakKey = _vm->_game->_script->readValExpr();
- flags = _vm->_game->_script->readValExpr();
- palStart = _vm->_game->_script->readValExpr();
- palEnd = _vm->_game->_script->readValExpr();
- palCmd = 1 << (flags & 0x3F);
+ VideoPlayer::Properties props;
+
+ props.x = _vm->_game->_script->readValExpr();
+ props.y = _vm->_game->_script->readValExpr();
+ props.startFrame = _vm->_game->_script->readValExpr();
+ props.lastFrame = _vm->_game->_script->readValExpr();
+ props.breakKey = _vm->_game->_script->readValExpr();
+ props.flags = _vm->_game->_script->readValExpr();
+ props.palStart = _vm->_game->_script->readValExpr();
+ props.palEnd = _vm->_game->_script->readValExpr();
+ props.palCmd = 1 << (props.flags & 0x3F);
+ props.forceSeek = true;
debugC(1, kDebugVideo, "Playing video \"%s\" @ %d+%d, frames %d - %d, "
- "paletteCmd %d (%d - %d), flags %X", fileName, x, y, startFrame, lastFrame,
- palCmd, palStart, palEnd, flags);
+ "paletteCmd %d (%d - %d), flags %X", fileName,
+ props.x, props.y, props.startFrame, props.lastFrame,
+ props.palCmd, props.palStart, props.palEnd, props.flags);
close = false;
- if (lastFrame == -1) {
+ if (props.lastFrame == -1) {
close = true;
- } else if (lastFrame == -5) {
+ } else if (props.lastFrame == -5) {
// warning("Urban/Playtoons Stub: Stop without delay");
_vm->_sound->bgStop();
return;
- } else if (lastFrame == -6) {
+ } else if (props.lastFrame == -6) {
// warning("Urban/Playtoons Stub: Video/Music command -6 (cache video)");
return;
- } else if (lastFrame == -7) {
+ } else if (props.lastFrame == -7) {
// warning("Urban/Playtoons Stub: Video/Music command -6 (flush cache)");
return;
- } else if ((lastFrame == -8) || (lastFrame == -9)) {
+ } else if ((props.lastFrame == -8) || (props.lastFrame == -9)) {
if (!strchr(fileName, '.'))
strcat(fileName, ".WA8");
probe16bitMusic(fileName);
- if (lastFrame == -9) {
+ if (props.lastFrame == -9) {
warning("Urban/Playtoons Stub: delayed stop not implemented");
}
_vm->_sound->bgStop();
_vm->_sound->bgPlay(fileName, SOUND_WAV);
return;
- } else if (lastFrame <= -10) {
- _vm->_vidPlayer->primaryClose();
- warning("Urban/Playtoons Stub: Video/Music command %d (close video?), %s", lastFrame, fileName);
- if (lastFrame <= -100)
- lastFrame += 100;
+ } else if (props.lastFrame <= -10) {
+ _vm->_vidPlayer->closeVideo();
+ warning("Urban/Playtoons Stub: Video/Music command %d (close video?), %s", props.lastFrame, fileName);
+ if (props.lastFrame <= -100)
+ props.lastFrame += 100;
- if (((-lastFrame) % 10 == 3) && (lastFrame <= -20))
+ if (((-props.lastFrame) % 10 == 3) && (props.lastFrame <= -20))
_vm->_sound->bgPlay(fileName, SOUND_WAV);
- } else if (lastFrame < 0) {
- warning("Urban/Playtoons Stub: Unknown Video/Music command: %d, %s", lastFrame, fileName);
+ } else if (props.lastFrame < 0) {
+ warning("Urban/Playtoons Stub: Unknown Video/Music command: %d, %s", props.lastFrame, fileName);
return;
}
- if (startFrame == -2) {
- startFrame = 0;
- lastFrame = -1;
+ if (props.startFrame == -2) {
+ props.startFrame = 0;
+ props.lastFrame = -1;
close = false;
}
- if ((fileName[0] != 0) && !_vm->_vidPlayer->primaryOpen(fileName, x, y, flags)) {
+ _vm->_vidPlayer->evaluateFlags(props);
+
+ int slot;
+ if ((fileName[0] != 0) && ((slot = _vm->_vidPlayer->openVideo(true, fileName, props)) < 0)) {
WRITE_VAR(11, (uint32) -1);
return;
}
- if (startFrame >= 0)
- _vm->_vidPlayer->primaryPlay(startFrame, lastFrame, breakKey,
- palCmd, palStart, palEnd, 0, -1, false, -1, true);
+ if (props.startFrame >= 0)
+ _vm->_vidPlayer->play(slot, props);
if (close)
- _vm->_vidPlayer->primaryClose();
+ _vm->_vidPlayer->closeVideo(slot);
}
void Inter_v6::o6_openItk() {
@@ -224,24 +222,30 @@ bool Inter_v6::o6_loadCursor(OpFuncParams &params) {
uint16 start = _vm->_game->_script->readUint16();
int8 index = _vm->_game->_script->readInt8();
- int vmdSlot = _vm->_vidPlayer->slotOpen(file);
+ VideoPlayer::Properties props;
+ props.sprite = -1;
+
+ int vmdSlot = _vm->_vidPlayer->openVideo(false, file, props);
if (vmdSlot == -1) {
warning("Can't open video \"%s\" as cursor", file);
return false;
}
- int16 framesCount = _vm->_vidPlayer->getFramesCount(vmdSlot);
+ int16 framesCount = _vm->_vidPlayer->getFrameCount(vmdSlot);
for (int i = 0; i < framesCount; i++) {
- _vm->_vidPlayer->slotPlay(vmdSlot);
- _vm->_vidPlayer->slotCopyFrame(vmdSlot, _vm->_draw->_cursorSprites->getVidMem(),
+ props.startFrame = i;
+ props.lastFrame = i;
+
+ _vm->_vidPlayer->play(vmdSlot, props);
+ _vm->_vidPlayer->copyFrame(vmdSlot, _vm->_draw->_cursorSprites->getVidMem(),
0, 0, _vm->_draw->_cursorWidth, _vm->_draw->_cursorWidth,
(start + i) * _vm->_draw->_cursorWidth, 0,
_vm->_draw->_cursorSprites->getWidth());
}
- _vm->_vidPlayer->slotClose(vmdSlot);
+ _vm->_vidPlayer->closeVideo(vmdSlot);
_vm->_draw->_cursorAnimLow[index] = start;
_vm->_draw->_cursorAnimHigh[index] = framesCount + start - 1;
diff --git a/engines/gob/mult.cpp b/engines/gob/mult.cpp
index 327b3ed1bd..f744f14faf 100644
--- a/engines/gob/mult.cpp
+++ b/engines/gob/mult.cpp
@@ -146,7 +146,7 @@ void Mult::freeMult() {
_orderArray = 0;
_animSurf.reset();
- _vm->_draw->freeSprite(22);
+ _vm->_draw->freeSprite(Draw::kAnimSurface);
}
void Mult::checkFreeMult() {
@@ -238,7 +238,7 @@ void Mult::playMult(int16 startFrame, int16 endFrame, char checkEscape,
_orderArray = 0;
_animSurf.reset();
- _vm->_draw->freeSprite(22);
+ _vm->_draw->freeSprite(Draw::kAnimSurface);
_animDataAllocated = false;
}
@@ -452,7 +452,7 @@ void Mult::clearObjectVideos() {
for (int i = 0; i < _objCount; i++)
if (_objects[i].videoSlot > 0)
- _vm->_vidPlayer->slotClose(_objects[i].videoSlot - 1);
+ _vm->_vidPlayer->closeVideo(_objects[i].videoSlot - 1);
}
} // End of namespace Gob
diff --git a/engines/gob/mult_v1.cpp b/engines/gob/mult_v1.cpp
index 1bb162c789..84869066e1 100644
--- a/engines/gob/mult_v1.cpp
+++ b/engines/gob/mult_v1.cpp
@@ -236,7 +236,7 @@ void Mult_v1::freeMultKeys() {
_animArrayData = 0;
_animSurf.reset();
- _vm->_draw->freeSprite(22);
+ _vm->_draw->freeSprite(Draw::kAnimSurface);
_animDataAllocated = false;
}
@@ -318,7 +318,7 @@ void Mult_v1::playMultInit() {
_animSurf = _vm->_video->initSurfDesc(_vm->_global->_videoMode,
320, 200, 0);
- _vm->_draw->_spritesArray[22] = _animSurf;
+ _vm->_draw->_spritesArray[Draw::kAnimSurface] = _animSurf;
_vm->_video->drawSprite(*_vm->_draw->_backSurface,
*_animSurf, 0, 0, 319, 199, 0, 0, 0);
@@ -579,8 +579,8 @@ void Mult_v1::animate() {
if ((pNeedRedraw[i] == 0) || (_objects[i].lastLeft == -1))
continue;
- _vm->_draw->_sourceSurface = 22;
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_sourceSurface = Draw::kAnimSurface;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_spriteLeft = pDirtyLefts[i] - _animLeft;
_vm->_draw->_spriteTop = pDirtyTops[i] - _animTop;
_vm->_draw->_spriteRight = pDirtyRights[i] - pDirtyLefts[i] + 1;
diff --git a/engines/gob/mult_v2.cpp b/engines/gob/mult_v2.cpp
index 88b604023c..66488054e7 100644
--- a/engines/gob/mult_v2.cpp
+++ b/engines/gob/mult_v2.cpp
@@ -579,11 +579,11 @@ void Mult_v2::playMultInit() {
width = _animWidth;
height = _animHeight;
_vm->_draw->adjustCoords(0, &width, &height);
- _vm->_draw->initSpriteSurf(22, width, height, 0);
- _animSurf = _vm->_draw->_spritesArray[22];
+ _vm->_draw->initSpriteSurf(Draw::kAnimSurface, width, height, 0);
+ _animSurf = _vm->_draw->_spritesArray[Draw::kAnimSurface];
- _vm->_video->drawSprite(*_vm->_draw->_spritesArray[21],
- *_vm->_draw->_spritesArray[22], 0, 0,
+ _vm->_video->drawSprite(*_vm->_draw->_spritesArray[Draw::kBackSurface],
+ *_vm->_draw->_spritesArray[Draw::kAnimSurface], 0, 0,
_vm->_video->_surfWidth, _vm->_video->_surfHeight, 0, 0, 0);
for (_counter = 0; _counter < _objCount; _counter++)
@@ -633,14 +633,14 @@ void Mult_v2::drawStatics(bool &stop) {
READ_LE_UINT16(_multData->execPtr + layer * 2);
_vm->_draw->_destSpriteX = 0;
_vm->_draw->_destSpriteY = 0;
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_transparency = 0;
_vm->_draw->spriteOperation(DRAW_LOADSPRITE);
_vm->_scenery->_curStatic = -1;
}
- _vm->_video->drawSprite(*_vm->_draw->_spritesArray[21],
- *_vm->_draw->_spritesArray[22], 0, 0,
+ _vm->_video->drawSprite(*_vm->_draw->_spritesArray[Draw::kBackSurface],
+ *_vm->_draw->_spritesArray[Draw::kAnimSurface], 0, 0,
_vm->_video->_surfWidth, _vm->_video->_surfHeight, 0, 0, 0);
}
}
@@ -710,7 +710,7 @@ void Mult_v2::newCycleAnim(Mult_Object &animObj) {
} else {
if (animObj.videoSlot > 0) {
_vm->_video->retrace();
- _vm->_vidPlayer->slotWaitEndFrame(animObj.videoSlot - 1, true);
+ _vm->_vidPlayer->waitEndFrame(animObj.videoSlot - 1, true);
}
}
@@ -736,8 +736,8 @@ void Mult_v2::newCycleAnim(Mult_Object &animObj) {
if (animData.animation < 0) {
if ((animObj.videoSlot > 0) &&
- (_vm->_vidPlayer->getCurrentFrame(animObj.videoSlot - 1) <
- _vm->_vidPlayer->getFramesCount(animObj.videoSlot - 1))) {
+ ((_vm->_vidPlayer->getCurrentFrame(animObj.videoSlot - 1) + 1) <
+ _vm->_vidPlayer->getFrameCount(animObj.videoSlot - 1))) {
animData.newCycle = 0;
return;
}
@@ -775,7 +775,7 @@ void Mult_v2::newCycleAnim(Mult_Object &animObj) {
animData.isStatic = 1;
animData.frame = 0;
if ((animData.animation < 0) && (animObj.videoSlot > 0)) {
- _vm->_vidPlayer->slotClose(animObj.videoSlot - 1);
+ _vm->_vidPlayer->closeVideo(animObj.videoSlot - 1);
animObj.videoSlot = 0;
}
@@ -788,7 +788,7 @@ void Mult_v2::newCycleAnim(Mult_Object &animObj) {
/*
if ((animData.animation < 0) && (animObj.videoSlot > 0)) {
if (_vm->_vidPlayer->getFlags(animObj.videoSlot - 1) & 0x1000) {
- _vm->_vidPlayer->slotClose(animObj.videoSlot - 1);
+ _vm->_vidPlayer->closeVideo(animObj.videoSlot - 1);
animObj.videoSlot = 0;
}
}
@@ -937,8 +937,8 @@ void Mult_v2::animate() {
if ((right <= 0) || (bottom <= 0))
continue;
- _vm->_draw->_sourceSurface = 22;
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_sourceSurface = Draw::kAnimSurface;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_spriteLeft = maxleft - _animLeft;
_vm->_draw->_spriteTop = maxtop - _animTop;
_vm->_draw->_spriteRight = right;
@@ -1100,56 +1100,66 @@ void Mult_v2::animate() {
void Mult_v2::playImd(const char *imdFile, Mult::Mult_ImdKey &key, int16 dir,
int16 startFrame) {
- int16 x, y;
- int16 palStart, palEnd;
- int16 baseFrame, palFrame, lastFrame;
- uint16 flags;
+
+ VideoPlayer::Properties props;
if (_vm->_draw->_renderFlags & 0x100) {
- x = VAR(55);
- y = VAR(56);
- } else
- x = y = -1;
+ props.x = VAR(55);
+ props.y = VAR(56);
+ }
if (key.imdFile == -1) {
- _vm->_vidPlayer->primaryClose();
+ _vm->_vidPlayer->closeVideo();
return;
}
- flags = (key.flags >> 8) & 0xFF;
- if (flags & 0x20)
- flags = (flags & 0x9F) | 0x80;
+ props.flags = (key.flags >> 8) & 0xFF;
+ if (props.flags & 0x20)
+ props.flags = (props.flags & 0x9F) | 0x80;
- palStart = key.palStart;
- palEnd = key.palEnd;
- palFrame = key.palFrame;
- lastFrame = key.lastFrame;
+ props.palStart = key.palStart;
+ props.palEnd = key.palEnd;
+ props.palFrame = key.palFrame;
+ props.lastFrame = key.lastFrame;
- if ((palFrame != -1) && (lastFrame != -1))
- if ((lastFrame - palFrame) < startFrame)
+ if ((props.palFrame != -1) && (props.lastFrame != -1))
+ if ((props.lastFrame - props.palFrame) < props.startFrame)
if (!(key.flags & 0x4000)) {
- _vm->_vidPlayer->primaryClose();
+ _vm->_vidPlayer->closeVideo();
return;
}
- if (!_vm->_vidPlayer->primaryOpen(imdFile, x, y, flags))
+ _vm->_vidPlayer->evaluateFlags(props);
+
+ int slot;
+ if ((slot = _vm->_vidPlayer->openVideo(true, imdFile, props)) < 0)
return;
- if (palFrame == -1)
- palFrame = 0;
+ if (props.palFrame == -1)
+ props.palFrame = 0;
+
+ if (props.lastFrame == -1)
+ props.lastFrame = _vm->_vidPlayer->getFrameCount() - 1;
+
+ uint32 baseFrame = startFrame % (props.lastFrame - props.palFrame + 1);
+
+ props.endFrame = props.lastFrame;
+ props.startFrame = baseFrame + props.palFrame;
+ props.lastFrame = baseFrame + props.palFrame;
+
+ props.flags &= 0x7F;
- if (lastFrame == -1)
- lastFrame = _vm->_vidPlayer->getFramesCount() - 1;
+ debugC(2, kDebugVideo, "Playing mult video \"%s\" @ %d+%d, frame %d, "
+ "paletteCmd %d (%d - %d; %d), flags %X", imdFile,
+ props.x, props.y, props.startFrame,
+ props.palCmd, props.palStart, props.palEnd, props.endFrame, props.flags);
- baseFrame = startFrame % (lastFrame - palFrame + 1);
- _vm->_vidPlayer->primaryPlay(baseFrame + palFrame, baseFrame + palFrame, 0,
- flags & 0x7F, palStart, palEnd, palFrame, lastFrame);
+ _vm->_vidPlayer->play(slot, props);
}
void Mult_v2::advanceObjects(int16 index) {
int16 frame;
bool stop = false;
- bool hasImds = false;
frame = _multData->animKeysFrames[index];
if (frame == -1)
@@ -1254,7 +1264,6 @@ void Mult_v2::advanceObjects(int16 index) {
if ((dir != 1) && (--startFrame < 0))
startFrame = 0;
- hasImds = true;
playImd(imdFile, key, dir, startFrame);
}
}
diff --git a/engines/gob/scenery.cpp b/engines/gob/scenery.cpp
index a6d6c06544..f9587dc0b3 100644
--- a/engines/gob/scenery.cpp
+++ b/engines/gob/scenery.cpp
@@ -261,10 +261,10 @@ void Scenery::renderStatic(int16 scenery, int16 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->_destSpriteX = 0;
+ _vm->_draw->_destSpriteY = 0;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
+ _vm->_draw->_transparency = 0;
_vm->_draw->spriteOperation(DRAW_LOADSPRITE);
}
@@ -295,7 +295,7 @@ void Scenery::renderStatic(int16 scenery, int16 layer) {
_vm->_draw->_sourceSurface =
_staticPictToSprite[scenery * 7 + pictIndex];
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_spriteLeft = left;
_vm->_draw->_spriteTop = top;
_vm->_draw->_spriteRight = right - left + 1;
@@ -392,7 +392,7 @@ void Scenery::updateStatic(int16 orderFrom, byte index, byte layer) {
_vm->_draw->_sourceSurface =
_staticPictToSprite[index * 7 + pictIndex];
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_transparency = planePtr->transp ? 3 : 0;
_vm->_draw->spriteOperation(DRAW_BLITSURF);
}
@@ -616,26 +616,29 @@ void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
return;
}
- if (frame >= _vm->_vidPlayer->getFramesCount(obj.videoSlot - 1))
- frame = _vm->_vidPlayer->getFramesCount(obj.videoSlot - 1) - 1;
+ if (frame >= (int32)_vm->_vidPlayer->getFrameCount(obj.videoSlot - 1))
+ frame = _vm->_vidPlayer->getFrameCount(obj.videoSlot - 1) - 1;
- // Seek to frame
- if (_vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1) < 256) {
- while (_vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1) <= frame)
- _vm->_vidPlayer->slotPlay(obj.videoSlot - 1);
- } else {
- int16 curFrame = _vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1);
- uint8 frameWrap = curFrame / 256;
- frame = (frame + 1) % 256;
+ if (frame != (int32)_vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1)) {
+ // Seek to frame
+
+ VideoPlayer::Properties props;
+
+ props.forceSeek = true;
+ props.waitEndFrame = false;
+ props.lastFrame = frame;
- while (_vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1) < (frameWrap * 256 + frame))
- _vm->_vidPlayer->slotPlay(obj.videoSlot - 1);
+ if ((int32)_vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1) < frame)
+ props.startFrame = _vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1) + 1;
+ else
+ props.startFrame = frame;
+
+ _vm->_vidPlayer->play(obj.videoSlot - 1, props);
}
- // Subtitle
- Graphics::CoktelVideo::State state = _vm->_vidPlayer->getState(obj.videoSlot - 1);
- if (state.flags & Graphics::CoktelVideo::kStateSpeech)
- _vm->_draw->printTotText(state.speechId);
+ int32 subtitle = _vm->_vidPlayer->getSubtitleIndex(obj.videoSlot - 1);
+ if (subtitle != -1)
+ _vm->_draw->printTotText(subtitle);
destX = 0;
destY = 0;
@@ -716,7 +719,7 @@ void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
_vm->_draw->_spriteLeft = _vm->_vidPlayer->getWidth(obj.videoSlot - 1) -
(destX + _vm->_draw->_spriteRight);
- _vm->_vidPlayer->slotCopyFrame(obj.videoSlot - 1, _vm->_draw->_backSurface->getVidMem(),
+ _vm->_vidPlayer->copyFrame(obj.videoSlot - 1, _vm->_draw->_backSurface->getVidMem(),
_vm->_draw->_spriteLeft, _vm->_draw->_spriteTop,
_vm->_draw->_spriteRight, _vm->_draw->_spriteBottom,
_vm->_draw->_destSpriteX, _vm->_draw->_destSpriteY,
@@ -726,13 +729,12 @@ void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
_vm->_draw->invalidateRect(_vm->_draw->_destSpriteX, _vm->_draw->_destSpriteY,
_vm->_draw->_destSpriteX + _vm->_draw->_spriteRight - 1,
_vm->_draw->_destSpriteY + _vm->_draw->_spriteBottom - 1);
-
}
if (!(flags & 4)) {
- _animLeft = _toRedrawLeft = left;
- _animTop = _toRedrawTop = top;
- _animRight = _toRedrawRight = right;
+ _animLeft = _toRedrawLeft = left;
+ _animTop = _toRedrawTop = top;
+ _animRight = _toRedrawRight = right;
_animBottom = _toRedrawBottom = bottom;
}
@@ -878,7 +880,7 @@ void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
if (doDraw) {
_vm->_draw->_sourceSurface =
_animPictToSprite[animation * 7 + pictIndex];
- _vm->_draw->_destSurface = 21;
+ _vm->_draw->_destSurface = Draw::kBackSurface;
_vm->_draw->_spriteLeft = left;
_vm->_draw->_spriteTop = top;
diff --git a/engines/gob/totfile.cpp b/engines/gob/totfile.cpp
index 5cc723ba7d..178deeaf58 100644
--- a/engines/gob/totfile.cpp
+++ b/engines/gob/totfile.cpp
@@ -49,7 +49,7 @@ bool TOTFile::load(const Common::String &fileName) {
if (!_stream)
// Trying to open from video
- _stream = _vm->_vidPlayer->getExtraData(fileName.c_str());
+ _stream = _vm->_vidPlayer->getEmbeddedFile(fileName.c_str());
if (!_stream)
return false;
diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index 125edd4307..9e49bfc092 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -29,7 +29,6 @@
#include "gob/global.h"
#include "gob/dataio.h"
#include "gob/video.h"
-#include "gob/draw.h"
#include "gob/game.h"
#include "gob/palanim.h"
#include "gob/inter.h"
@@ -38,767 +37,723 @@
namespace Gob {
-const char *VideoPlayer::_extensions[] = { "IMD", "IMD", "VMD", "RMD", "SMD" };
-
-VideoPlayer::Video::Video(GobEngine *vm) : _vm(vm), _stream(0), _video(0) {
-}
-
-VideoPlayer::Video::~Video() {
- close();
-}
-
-bool VideoPlayer::Video::open(const char *fileName, Type which, int16 width, int16 height) {
- close();
-
- int16 handle = _vm->_dataIO->openData(fileName);
-
- if (handle < 0) {
- warning("Couldn't open video \"%s\": No such file", fileName);
- return false;
- }
-
- _stream = _vm->_dataIO->openAsStream(handle, true);
-
- if (which == kVideoTypeIMD) {
- _video = new Graphics::Imd();
- } else if (which == kVideoTypePreIMD) {
- _video = new Graphics::PreImd(width, height);
- } else if (which == kVideoTypeVMD) {
- _video = new Graphics::Vmd(_vm->_video->_palLUT);
- } else if (which == kVideoTypeRMD) {
- _video = new Graphics::Vmd(_vm->_video->_palLUT);
- } else {
- warning("Couldn't open video \"%s\": Invalid video Type", fileName);
- close();
- return false;
- }
-
- if (!_video->load(*_stream)) {
- warning("While loading video \"%s\"", fileName);
- close();
- return false;
- }
-
- _fileName = fileName;
+VideoPlayer::Properties::Properties() : type(kVideoTypeTry), sprite(Draw::kFrontSurface),
+ x(-1), y(-1), width(-1), height(-1), flags(kFlagFrontSurface),
+ startFrame(-1), lastFrame(-1), endFrame(-1), forceSeek(false),
+ breakKey(kShortKeyEscape), palCmd(8), palStart(0), palEnd(255), palFrame(-1),
+ fade(false), waitEndFrame(true), canceled(false) {
- _defaultX = _video->getX();
- _defaultY = _video->getY();
-
- return true;
}
-void VideoPlayer::Video::close() {
- delete _video;
- delete _stream;
-
- _video = 0;
- _stream = 0;
- _fileName.clear();
- memset(&_state, 0, sizeof(Graphics::CoktelVideo::State));
- _defaultX = _defaultY = 0;
-}
-bool VideoPlayer::Video::isOpen() const {
- return (_video != 0);
+VideoPlayer::Video::Video() : decoder(0) {
}
-const char *VideoPlayer::Video::getFileName() const {
- return _fileName.c_str();
+bool VideoPlayer::Video::isEmpty() const {
+ return decoder == 0;
}
-Graphics::CoktelVideo *VideoPlayer::Video::getVideo() {
- return _video;
-}
+void VideoPlayer::Video::close() {
+ delete decoder;
-const Graphics::CoktelVideo *VideoPlayer::Video::getVideo() const {
- return _video;
+ decoder = 0;
+ fileName.clear();
+ surface.reset();
}
-uint32 VideoPlayer::Video::getFeatures() const {
- return _video->getFeatures();
-}
-Graphics::CoktelVideo::State VideoPlayer::Video::getState() const {
- return _state;
-}
+const char *VideoPlayer::_extensions[] = { "IMD", "IMD", "VMD", "RMD", "SMD" };
-int16 VideoPlayer::Video::getDefaultX() const {
- return _defaultX;
+VideoPlayer::VideoPlayer(GobEngine *vm) : _vm(vm), _needBlit(false),
+ _noCursorSwitch(false), _woodruffCohCottWorkaround(false) {
}
-int16 VideoPlayer::Video::getDefaultY() const {
- return _defaultY;
+VideoPlayer::~VideoPlayer() {
+ for (int i = 0; i < kVideoSlotCount; i++)
+ _videoSlots[i].close();
+}
+
+void VideoPlayer::evaluateFlags(Properties &properties) {
+ if (properties.flags & kFlagFrontSurface) {
+ properties.sprite = Draw::kFrontSurface;
+ } else if (properties.flags & kFlagOtherSurface) {
+ properties.sprite = properties.x;
+ properties.x = 0;
+ } else if (properties.flags & kFlagScreenSurface) {
+ properties.sprite = 0;
+ } else if (properties.flags & kFlagNoVideo) {
+ properties.sprite = 0;
+ } else {
+ properties.sprite = Draw::kBackSurface;
+ }
}
-bool VideoPlayer::Video::hasExtraData(const char *fileName) const {
- if (!_video)
- return false;
+int VideoPlayer::openVideo(bool primary, const Common::String &file, Properties &properties) {
+ int slot = 0;
- return _video->hasExtraData(fileName);
-}
-
-Common::MemoryReadStream *VideoPlayer::Video::getExtraData(const char *fileName) {
- if (!_video)
- return 0;
+ Video *video = 0;
+ if (!primary) {
+ slot = getNextFreeSlot();
+ if (slot < 0) {
+ warning("VideoPlayer::openVideo(): Can't open video \"%s\": No free slot", file.c_str());
+ return -1;
+ }
- return _video->getExtraData(fileName);
-}
+ video = &_videoSlots[slot];
+ } else
+ video = &_videoSlots[0];
-Graphics::CoktelVideo::State VideoPlayer::Video::nextFrame() {
- if (_video)
- _state = _video->nextFrame();
+ // Different video already in the slot => close that video
+ if (!video->isEmpty() && (video->fileName.compareToIgnoreCase(file) != 0))
+ video->close();
- return _state;
-}
+ // No video => load the requested file
+ if (video->isEmpty()) {
+ // Open the video
+ if (!(video->decoder = openVideo(file, properties)))
+ return -1;
+ // Set the filename
+ video->fileName = file;
-VideoPlayer::VideoPlayer(GobEngine *vm) : _vm(vm) {
- _primaryVideo = new Video(vm);
- _ownSurf = false;
- _backSurf = false;
- _needBlit = false;
- _noCursorSwitch = false;
- _woodruffCohCottWorkaround = false;
-}
+ // WORKAROUND: In some rare cases, the cursor should still be
+ // displayed while a video is playing.
+ _noCursorSwitch = false;
+ if (primary && (_vm->getGameType() == kGameTypeLostInTime)) {
+ if (!file.compareToIgnoreCase("PORTA03") ||
+ !file.compareToIgnoreCase("PORTA03A") ||
+ !file.compareToIgnoreCase("CALE1") ||
+ !file.compareToIgnoreCase("AMIL2") ||
+ !file.compareToIgnoreCase("AMIL3B") ||
+ !file.compareToIgnoreCase("DELB"))
+ _noCursorSwitch = true;
+ }
-VideoPlayer::~VideoPlayer() {
- delete _primaryVideo;
- for (uint i = 0; i < _videoSlots.size(); i++)
- delete _videoSlots[i];
-}
+ // WORKAROUND: In Woodruff, Coh Cott vanished in one video on her party.
+ // This is a bug in video, so we work around it.
+ _woodruffCohCottWorkaround = false;
+ if (primary && (_vm->getGameType() == kGameTypeWoodruff)) {
+ if (!file.compareToIgnoreCase("SQ32-03"))
+ _woodruffCohCottWorkaround = true;
+ }
-bool VideoPlayer::findFile(char *fileName, Type &which) {
- char *extStart = strrchr(fileName, '.');
- // There's no empty extension, Or the filename with its current extension is not found
- if ((extStart) && ((extStart == (fileName + strlen(fileName) - 1)) || (!_vm->_dataIO->existData(fileName)))) {
- *extStart = 0;
- extStart = 0;
- }
+ if (!(properties.flags & kFlagNoVideo) && (properties.sprite >= 0)) {
+ bool ownSurf = (properties.sprite != Draw::kFrontSurface) && (properties.sprite != Draw::kBackSurface);
+ bool screenSize = properties.flags & kFlagScreenSurface;
- if (extStart) {
- // The requested file already has an extension. Verifying.
+ if (ownSurf) {
+ _vm->_draw->_spritesArray[properties.sprite] =
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode,
+ screenSize ? _vm->_width : video->decoder->getWidth(),
+ screenSize ? _vm->_height : video->decoder->getHeight(), 0);
+ }
- int i;
- for (i = 0; i < ARRAYSIZE(_extensions); i++) {
- if (!scumm_stricmp(extStart + 1, _extensions[i])) {
- if ((which != kVideoTypeTry) && (which == ((Type) i))) {
- warning("Attempted to open video \"%s\", "
- "but requested a different type", fileName);
- return false;
- }
- which = (Type) i;
- break;
+ if (!_vm->_draw->_spritesArray[properties.sprite]) {
+ properties.sprite = -1;
+ video->surface.reset();
+ video->decoder->setSurfaceMemory();
+ video->decoder->setXY(0, 0);
+ } else {
+ video->surface = _vm->_draw->_spritesArray[properties.sprite];
+ video->decoder->setSurfaceMemory(video->surface->getVidMem(),
+ video->surface->getWidth(), video->surface->getHeight(), 1);
+
+ if (!ownSurf || (ownSurf && screenSize)) {
+ if ((properties.x >= 0) || (properties.y >= 0))
+ video->decoder->setXY((properties.x < 0) ? 0xFFFF : properties.x,
+ (properties.y < 0) ? 0xFFFF : properties.y);
+ else
+ video->decoder->setXY();
+ } else
+ video->decoder->setXY(0, 0);
}
- }
- if (i >= ARRAYSIZE(_extensions))
- extStart = 0;
+ } else {
+ properties.sprite = -1;
+ video->surface.reset();
+ video->decoder->setSurfaceMemory();
+ video->decoder->setXY(0, 0);
+ }
}
- if (!extStart) {
- // No or unrecognized extension. Probing.
+ if (primary)
+ _needBlit = (properties.flags & kFlagUseBackSurfaceContent) && (properties.sprite == Draw::kFrontSurface);
- int len = strlen(fileName);
+ if (!video->decoder->hasSound())
+ video->decoder->setFrameRate(_vm->_util->getFrameRate());
- int i;
- for (i = 0; i < ARRAYSIZE(_extensions); i++) {
- if ((which == kVideoTypeTry) || (which == ((Type) i))) {
- fileName[len] = '.';
- fileName[len + 1] = 0;
- strcat(fileName, _extensions[i]);
+ WRITE_VAR(7, video->decoder->getFrameCount());
- if (_vm->_dataIO->existData(fileName)) {
- which = (Type) i;
- break;
- }
- }
- }
- if ((i >= ARRAYSIZE(_extensions)) || (which == kVideoTypeTry)) {
- fileName[len] = 0;
- warning("Couldn't open video \"%s\"", fileName);
- return false;
- }
+ return slot;
+}
- }
+bool VideoPlayer::closeVideo(int slot) {
+ Video *video = getVideoBySlot(slot);
+ if (!video)
+ return false;
+ video->close();
return true;
}
-bool VideoPlayer::primaryOpen(const char *videoFile, int16 x, int16 y,
- int32 flags, Type which, int16 width, int16 height) {
-
- char fileName[256];
-
- strncpy0(fileName, videoFile, 250);
-
- if (!findFile(fileName, which))
+bool VideoPlayer::play(int slot, Properties &properties) {
+ Video *video = getVideoBySlot(slot);
+ if (!video)
return false;
- if (scumm_strnicmp(_primaryVideo->getFileName(), fileName, strlen(fileName))) {
- if (!_primaryVideo->open(fileName, which, width, height))
- return false;
+ bool primary = slot == 0;
- // WORKAROUND: In some rare cases, the cursor should still be
- // displayed while a video is playing.
- _noCursorSwitch = false;
- if (_vm->getGameType() == kGameTypeLostInTime) {
- if (!scumm_stricmp(fileName, "PORTA03.IMD") ||
- !scumm_stricmp(fileName, "PORTA03A.IMD") ||
- !scumm_stricmp(fileName, "CALE1.IMD") ||
- !scumm_stricmp(fileName, "AMIL2.IMD") ||
- !scumm_stricmp(fileName, "AMIL3B.IMD") ||
- !scumm_stricmp(fileName, "DELB.IMD"))
- _noCursorSwitch = true;
- }
+ // NOTE: For testing (and comfort?) purposes, we enable aborting of all videos)
+ properties.breakKey = kShortKeyEscape;
- // WORKAROUND: In Woodruff, Coh Cott vanished in one video on her party.
- // This is a bug in video, so we work around it.
- _woodruffCohCottWorkaround = false;
- if (_vm->getGameType() == kGameTypeWoodruff) {
- if (!scumm_stricmp(fileName, "SQ32-03.VMD"))
- _woodruffCohCottWorkaround = true;
- }
+ if (properties.startFrame < 0)
+ properties.startFrame = video->decoder->getCurFrame() + 1;
+ if (properties.lastFrame < 0)
+ properties.lastFrame = video->decoder->getFrameCount() - 1;
+ if (properties.endFrame < 0)
+ properties.endFrame = properties.lastFrame;
+ if (properties.palFrame < 0)
+ properties.palFrame = properties.startFrame;
- _ownSurf = false;
+ properties.startFrame--;
+ properties.endFrame--;
+ properties.palFrame--;
- if (!(flags & kFlagNoVideo)) {
- SurfaceDescPtr surf;
+ if (primary) {
+ _vm->_draw->_showCursor = _noCursorSwitch ? 3 : 0;
- if (flags & kFlagOtherSurface) {
- _ownSurf = true;
- _backSurf = false;
+ if (properties.fade)
+ _vm->_palAnim->fade(0, -2, 0);
+ }
- surf = _vm->_video->initSurfDesc(_vm->_global->_videoMode,
- _primaryVideo->getVideo()->getWidth(),
- _primaryVideo->getVideo()->getHeight(), 0);
- _vm->_draw->_spritesArray[x] = surf;
+ bool backwards = properties.startFrame > properties.lastFrame;
- x = 0;
- } else if (flags & kFlagScreenSurface) {
- _ownSurf = true;
- _backSurf = false;
+ properties.canceled = false;
- surf = _vm->_video->initSurfDesc(_vm->_global->_videoMode,
- _vm->_width, _vm->_height, 0);
- _vm->_draw->_spritesArray[0] = surf;
- } else {
- _backSurf = ((flags & kFlagFrontSurface) == 0);
- surf = _vm->_draw->_spritesArray[_backSurf ? 21 : 20];
- }
+ while ((properties.startFrame != properties.lastFrame) &&
+ (properties.startFrame < (int32)(video->decoder->getFrameCount() - 1))) {
- _primaryVideo->getVideo()->setVideoMemory(surf->getVidMem(),
- surf->getWidth(), surf->getHeight());
+ playFrame(slot, properties);
+ if (properties.canceled)
+ break;
- } else
- _primaryVideo->getVideo()->setVideoMemory();
+ properties.startFrame += backwards ? -1 : 1;
- _needBlit = ((flags & kFlagUseBackSurfaceContent) != 0) && ((flags & kFlagFrontSurface) != 0);
+ evalBgShading(*video);
- _primaryVideo->getVideo()->enableSound(*_vm->_mixer);
- }
+ if (primary && properties.fade) {
+ _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
+ properties.fade = false;
+ }
- if (!_primaryVideo->isOpen())
- return false;
+ if (!_noCursorSwitch && properties.waitEndFrame)
+ waitEndFrame(slot);
+ }
- _primaryVideo->getVideo()->setFrameRate(_vm->_util->getFrameRate());
- _primaryVideo->getVideo()->setXY(x, y);
- WRITE_VAR(7, _primaryVideo->getVideo()->getFramesCount());
+ evalBgShading(*video);
return true;
}
-bool VideoPlayer::primaryPlay(int16 startFrame, int16 lastFrame, int16 breakKey,
- uint16 palCmd, int16 palStart, int16 palEnd,
- int16 palFrame, int16 endFrame, bool fade, int16 reverseTo, bool forceSeek) {
+void VideoPlayer::waitEndFrame(int slot, bool onlySound) {
+ Video *video = getVideoBySlot(slot);
+ if (!video)
+ return;
+
+ if (!onlySound || video->decoder->hasSound())
+ _vm->_util->delay(video->decoder->getTimeToNextFrame());
+}
- if (!_primaryVideo->isOpen())
+bool VideoPlayer::playFrame(int slot, Properties &properties) {
+ Video *video = getVideoBySlot(slot);
+ if (!video)
return false;
- Graphics::CoktelVideo &video = *(_primaryVideo->getVideo());
-
- breakKey = 27;
- if (startFrame < 0)
- startFrame = video.getCurrentFrame();
- if (lastFrame < 0)
- lastFrame = video.getFramesCount() - 1;
- if (palFrame < 0)
- palFrame = startFrame;
- if (endFrame < 0)
- endFrame = lastFrame;
- palCmd &= 0x3F;
-
- int16 realStartFrame = startFrame;
- if (video.getCurrentFrame() != startFrame) {
- if (!forceSeek && (video.getFeatures() & Graphics::CoktelVideo::kFeaturesSound))
- startFrame = video.getCurrentFrame();
- else
- video.seekFrame(startFrame);
- }
+ bool primary = slot == 0;
- _vm->_draw->_showCursor = _noCursorSwitch ? 3 : 0;
+ if (video->decoder->getCurFrame() != properties.startFrame) {
- if (fade)
- _vm->_palAnim->fade(0, -2, 0);
+ if (properties.startFrame != -1) {
+ // Seek into the middle of the video
- bool canceled = false;
+ if (video->decoder->hasSound()) {
+ // But there's sound
- while (startFrame <= lastFrame) {
- if (doPlay(startFrame, breakKey,
- palCmd, palStart, palEnd, palFrame, endFrame, startFrame < realStartFrame)) {
+ if (properties.forceSeek) {
+ // And we force seeking => Seek
- canceled = true;
- break;
- }
+ video->decoder->disableSound();
+ video->decoder->seek(properties.startFrame + 1, SEEK_SET, true);
+ }
- evalBgShading(video);
+ } else
+ // No sound => We can safely seek
+ video->decoder->seek(properties.startFrame + 1, SEEK_SET, true);
- if (fade) {
- _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
- fade = false;
+ } else {
+ // Seek to the start => We can safely seek
+
+ video->decoder->disableSound();
+ video->decoder->seek(0, SEEK_SET, true);
+ video->decoder->enableSound();
}
- if (!_noCursorSwitch)
- video.waitEndFrame();
- startFrame++;
}
- evalBgShading(video);
+ if (video->decoder->getCurFrame() > properties.startFrame)
+ // If the video is already beyond the wanted frame, skip
+ return true;
- if (reverseTo >= 0) {
- int16 toFrame = video.getFramesCount() - reverseTo;
- for (int i = video.getCurrentFrame(); i >= toFrame; i--) {
- video.seekFrame(i, SEEK_SET, true);
+ bool modifiedPal = false;
- bool b = doPlay(i, breakKey, 0, 0, 0, 0, 0);
- evalBgShading(video);
+ if (primary) {
+ // Pre-decoding palette and blitting, only for primary videos
- if (b) {
- _vm->_palAnim->fade(0, -2, 0);
- memset((char *)_vm->_draw->_vgaPalette, 0, 768);
- }
+ if ((properties.startFrame == properties.palFrame) ||
+ ((properties.startFrame == properties.endFrame) && (properties.palCmd == 8))) {
- if (!_noCursorSwitch)
- video.waitEndFrame();
- }
- }
+ modifiedPal = true;
+ _vm->_draw->_applyPal = true;
- evalBgShading(video);
+ if (properties.palCmd >= 4)
+ copyPalette(*video, properties.palStart, properties.palEnd);
+ }
- return canceled;
-}
+ if (modifiedPal && (properties.palCmd == 8) && (video->surface != _vm->_draw->_backSurface))
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
-void VideoPlayer::primaryClose() {
- _primaryVideo->close();
-}
+ if (_needBlit)
+ _vm->_draw->forceBlit();
+ }
-int VideoPlayer::slotOpen(const char *videoFile, Type which, int16 width, int16 height) {
- Video *video = new Video(_vm);
- char fileName[256];
+ Graphics::Surface *surface = video->decoder->decodeNextFrame();
- strncpy0(fileName, videoFile, 250);
+ WRITE_VAR(11, video->decoder->getCurFrame());
- if (!findFile(fileName, which)) {
- delete video;
- return -1;
- }
-
- if (!video->open(fileName, which, width, height)) {
- delete video;
- return -1;
+ uint32 ignoreBorder = 0;
+ if (_woodruffCohCottWorkaround && (properties.startFrame == 31)) {
+ // WORKAROUND: This frame mistakenly masks Coh Cott, making her vanish
+ // To prevent that, we'll never draw that part
+ ignoreBorder = 50;
}
- video->getVideo()->setVideoMemory();
- video->getVideo()->enableSound(*_vm->_mixer);
+ if (surface && primary) {
+ // Post-decoding palette and blitting, only for primary videos
- int slot = getNextFreeSlot();
+ if (_needBlit)
+ _vm->_draw->forceBlit(true);
- _videoSlots[slot] = video;
+ if (modifiedPal && (properties.palCmd == 16)) {
+ if (video->surface == _vm->_draw->_backSurface)
+ _vm->_draw->forceBlit();
+ _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
+ _vm->_draw->_noInvalidated = true;
+ _vm->_video->dirtyRectsAll();
+ }
- WRITE_VAR(7, video->getVideo()->getFramesCount());
+ if (video->decoder->hasPalette() && (properties.palCmd > 1)) {
+ copyPalette(*video, properties.palStart, properties.palEnd);
- return slot;
-}
+ if (video->surface != _vm->_draw->_backSurface)
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ else
+ _vm->_draw->_applyPal = true;
+ }
-int VideoPlayer::getNextFreeSlot() {
- uint slot;
+ const Common::List<Common::Rect> &dirtyRects = video->decoder->getDirtyRects();
- for (slot = 0; slot < _videoSlots.size(); slot++)
- if (!_videoSlots[slot])
- break;
+ if (modifiedPal && (properties.palCmd == 8) && (video->surface == _vm->_draw->_backSurface))
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
- if (slot == _videoSlots.size())
- _videoSlots.push_back(0);
+ if (video->surface == _vm->_draw->_backSurface) {
- return slot;
-}
-
-void VideoPlayer::slotPlay(int slot, int16 frame) {
- if ((slot < 0) || (((uint) slot) >= _videoSlots.size()) || !_videoSlots[slot])
- return;
+ for (Common::List<Common::Rect>::const_iterator rect = dirtyRects.begin(); rect != dirtyRects.end(); ++rect)
+ _vm->_draw->invalidateRect(rect->left + ignoreBorder, rect->top, rect->right - 1, rect->bottom - 1);
+ _vm->_draw->blitInvalidated();
- Graphics::CoktelVideo &video = *(_videoSlots[slot]->getVideo());
+ } else if (video->surface == _vm->_draw->_frontSurface) {
+ for (Common::List<Common::Rect>::const_iterator rect = dirtyRects.begin(); rect != dirtyRects.end(); ++rect)
+ _vm->_video->dirtyRectsAdd(rect->left + ignoreBorder, rect->top, rect->right - 1, rect->bottom - 1);
- if (frame < 0)
- frame = video.getCurrentFrame();
+ }
- if (frame >= video.getFramesCount())
- return;
+ if ((video->decoder->getCurFrame() - 1) == properties.startFrame)
+ // Only retrace if we're playing the frame we actually want to play
+ _vm->_video->retrace();
- if (video.getCurrentFrame() != frame)
- video.seekFrame(frame);
+ int32 subtitle = video->decoder->getSubtitleIndex();
+ if (subtitle != -1)
+ _vm->_draw->printTotText(subtitle);
- _videoSlots[slot]->nextFrame();
- WRITE_VAR(11, frame);
+ if (modifiedPal && ((properties.palCmd == 2) || (properties.palCmd == 4)))
+ _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
+ }
- evalBgShading(video);
-}
+ if (primary && properties.waitEndFrame)
+ checkAbort(*video, properties);
-void VideoPlayer::slotClose(int slot) {
- if ((slot < 0) || (((uint) slot) >= _videoSlots.size()) || !_videoSlots[slot])
- return;
+ if ((video->decoder->getCurFrame() - 1) < properties.startFrame)
+ // The video played a frame we actually didn't want, so we have to adjust
+ properties.startFrame--;
- delete _videoSlots[slot];
- _videoSlots[slot] = 0;
+ return true;
}
-void VideoPlayer::slotCopyFrame(int slot, byte *dest,
- uint16 left, uint16 top, uint16 width, uint16 height,
- uint16 x, uint16 y, uint16 pitch, int16 transp) {
-
- if ((slot < 0) || (((uint) slot) >= _videoSlots.size()) || !_videoSlots[slot])
- return;
+void VideoPlayer::checkAbort(Video &video, Properties &properties) {
+ _vm->_util->processInput();
- _videoSlots[slot]->getVideo()->copyCurrentFrame(dest,
- left, top, width, height, x, y, pitch, transp);
-}
+ if (_vm->shouldQuit()) {
+ video.decoder->disableSound();
-void VideoPlayer::slotCopyPalette(int slot, int16 palStart, int16 palEnd) {
- if ((slot < 0) || (((uint) slot) >= _videoSlots.size()) || !_videoSlots[slot])
+ properties.canceled = true;
return;
+ }
- copyPalette(*(_videoSlots[slot]->getVideo()), palStart, palEnd);
-}
+ if (properties.breakKey != 0) {
+ _vm->_util->getMouseState(&_vm->_global->_inter_mouseX,
+ &_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons);
-void VideoPlayer::slotWaitEndFrame(int slot, bool onlySound) {
- Video *video = getVideoBySlot(slot);
+ _vm->_inter->storeKey(_vm->_util->checkKey());
+ if (VAR(0) == (unsigned) properties.breakKey) {
+ video.decoder->disableSound();
- if (video) {
- Graphics::CoktelVideo &cVideo = *video->getVideo();
+ // Seek to the last frame. Some scripts depend on that.
+ video.decoder->seek(properties.endFrame + 1, SEEK_SET, true);
- if (!onlySound || (cVideo.getFeatures() & Graphics::CoktelVideo::kFeaturesSound))
- cVideo.waitEndFrame();
+ properties.canceled = true;
+ }
}
}
bool VideoPlayer::slotIsOpen(int slot) const {
- if ((slot >= 0) && (((uint) slot) < _videoSlots.size()) && _videoSlots[slot])
- return true;
-
- return false;
-}
-
-void VideoPlayer::slotSetDoubleMode(int slot, bool doubleMode) {
- Video *video = getVideoBySlot(slot);
-
- if (video)
- video->getVideo()->setDoubleMode(doubleMode);
+ return getVideoBySlot(slot) != 0;
}
-const VideoPlayer::Video *VideoPlayer::getVideoBySlot(int slot) const {
- if (slot < 0) {
- if (_primaryVideo->isOpen())
- return _primaryVideo;
- } else if (((uint) slot) < _videoSlots.size() && _videoSlots[slot])
- return _videoSlots[slot];
+Common::String VideoPlayer::getFileName(int slot) const {
+ const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return "";
- return 0;
+ return video->fileName;
}
-VideoPlayer::Video *VideoPlayer::getVideoBySlot(int slot) {
- if (slot < 0) {
- if (_primaryVideo->isOpen())
- return _primaryVideo;
- } else if (((uint) slot) < _videoSlots.size() && _videoSlots[slot])
- return _videoSlots[slot];
+uint32 VideoPlayer::getFrameCount(int slot) const {
+ const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- return 0;
+ return video->decoder->getFrameCount();
}
-const char *VideoPlayer::getFileName(int slot) const {
+uint32 VideoPlayer::getCurrentFrame(int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- if (video)
- return video->getFileName();
-
- return "";
+ return video->decoder->getCurFrame();
}
-uint16 VideoPlayer::getFlags(int slot) const {
+uint16 VideoPlayer::getWidth(int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- if (video)
- return video->getVideo()->getFlags();
-
- return 0;
+ return video->decoder->getWidth();
}
-int16 VideoPlayer::getFramesCount(int slot) const {
+uint16 VideoPlayer::getHeight(int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- if (video)
- return video->getVideo()->getFramesCount();
-
- return 0;
+ return video->decoder->getHeight();
}
-int16 VideoPlayer::getCurrentFrame(int slot) const {
+uint16 VideoPlayer::getDefaultX(int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- if (video)
- return video->getVideo()->getCurrentFrame();
-
- return 0;
+ return video->decoder->getDefaultX();
}
-int16 VideoPlayer::getWidth(int slot) const {
+uint16 VideoPlayer::getDefaultY(int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- if (video)
- return video->getVideo()->getWidth();
-
- return 0;
+ return video->decoder->getDefaultY();
}
-int16 VideoPlayer::getHeight(int slot) const {
+const Common::List<Common::Rect> *VideoPlayer::getDirtyRects(int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- if (video)
- return video->getVideo()->getHeight();
-
- return 0;
+ return &video->decoder->getDirtyRects();
}
-int16 VideoPlayer::getDefaultX(int slot) const {
+bool VideoPlayer::hasEmbeddedFile(const Common::String &fileName, int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return false;
- if (video)
- return video->getDefaultX();
-
- return 0;
+ return video->decoder->hasEmbeddedFile(fileName);
}
-int16 VideoPlayer::getDefaultY(int slot) const {
+Common::MemoryReadStream *VideoPlayer::getEmbeddedFile(const Common::String &fileName, int slot) {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return 0;
- if (video)
- return video->getDefaultY();
-
- return 0;
+ return video->decoder->getEmbeddedFile(fileName);
}
-uint32 VideoPlayer::getFeatures(int slot) const {
+int32 VideoPlayer::getSubtitleIndex(int slot) const {
const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return -1;
- if (video)
- return video->getFeatures();
-
- return 0;
+ return video->decoder->getSubtitleIndex();
}
-Graphics::CoktelVideo::State VideoPlayer::getState(int slot) const {
- const Video *video = getVideoBySlot(slot);
- Graphics::CoktelVideo::State state;
+void VideoPlayer::writeVideoInfo(const Common::String &file, int16 varX, int16 varY,
+ int16 varFrames, int16 varWidth, int16 varHeight) {
- if (video)
- state = video->getState();
+ Properties properties;
- return state;
-}
+ int slot = openVideo(false, file, properties);
+ if (slot >= 0) {
+ Video &video = _videoSlots[slot];
-bool VideoPlayer::hasExtraData(const char *fileName, int slot) const {
- const Video *video = getVideoBySlot(slot);
+ int16 x = -1, y = -1, width = -1, height = -1;
- if (video)
- return video->hasExtraData(fileName);
+ x = video.decoder->getDefaultX();
+ y = video.decoder->getDefaultY();
+ width = video.decoder->getWidth();
+ height = video.decoder->getHeight();
- return false;
-}
+ if (VAR_OFFSET(varX) == 0xFFFFFFFF)
+ video.decoder->getFrameCoords(1, x, y, width, height);
-Common::MemoryReadStream *VideoPlayer::getExtraData(const char *fileName, int slot) {
- Video *video = getVideoBySlot(slot);
+ WRITE_VAR_OFFSET(varX , x);
+ WRITE_VAR_OFFSET(varY , y);
+ WRITE_VAR_OFFSET(varFrames, video.decoder->getFrameCount());
+ WRITE_VAR_OFFSET(varWidth , width);
+ WRITE_VAR_OFFSET(varHeight, height);
- if (video)
- return video->getExtraData(fileName);
+ closeVideo(slot);
- return 0;
+ } else {
+ WRITE_VAR_OFFSET(varX , (uint32) -1);
+ WRITE_VAR_OFFSET(varY , (uint32) -1);
+ WRITE_VAR_OFFSET(varFrames, (uint32) -1);
+ WRITE_VAR_OFFSET(varWidth , (uint32) -1);
+ WRITE_VAR_OFFSET(varHeight, (uint32) -1);
+ }
}
-void VideoPlayer::playFrame(int16 frame, int16 breakKey,
- uint16 palCmd, int16 palStart, int16 palEnd,
- int16 palFrame, int16 endFrame, bool noRetrace) {
+bool VideoPlayer::copyFrame(int slot, byte *dest,
+ uint16 left, uint16 top, uint16 width, uint16 height,
+ uint16 x, uint16 y, uint16 pitch, int16 transp) const {
- if (!_primaryVideo)
- return;
+ const Video *video = getVideoBySlot(slot);
+ if (!video)
+ return false;
- Video &video = *_primaryVideo;
- Graphics::CoktelVideo &cVideo = *video.getVideo();
+ const Graphics::Surface *surface = video->decoder->getSurface();
+ if (!surface)
+ return false;
- if (cVideo.getCurrentFrame() != frame)
- cVideo.seekFrame(frame);
- if (palFrame < 0)
- palFrame = 0;
- if (endFrame < 0)
- endFrame = cVideo.getFramesCount() - 1;
+ int32 w = MIN<int32>(width , surface->w);
+ int32 h = MIN<int32>(height, surface->h);
+ const byte *src = (byte*)surface->pixels + (top * surface->pitch) + left;
+ byte *dst = dest + (y * pitch) + x;
- bool modifiedPal = false;
+ if (transp < 0) {
+ // No transparency
- if ((frame == palFrame) || ((frame == endFrame) && (palCmd == 8))) {
- modifiedPal = true;
- _vm->_draw->_applyPal = true;
+ if ((x == 0) && (left == 0) && (pitch == surface->pitch) && (width == surface->w)) {
+ // Dimensions fit, we can copy everything at once
- if (palCmd >= 4)
- copyPalette(cVideo, palStart, palEnd);
- }
+ memcpy(dst, src, w * h);
+ return true;
+ }
- if (modifiedPal && (palCmd == 8) && !_backSurf)
- _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ // Copy row-by-row
+ while (h-- > 0) {
+ const byte *srcRow = src;
+ byte *dstRow = dst;
- if (_needBlit)
- _vm->_draw->forceBlit();
+ memcpy(dstRow, srcRow, w);
- Graphics::CoktelVideo::State state = video.nextFrame();
- WRITE_VAR(11, frame);
+ src += surface->pitch;
+ dst += pitch;
+ }
- if (_woodruffCohCottWorkaround && (frame == 32)) {
- // WORKAROUND: This frame mistakenly masks Coh Cott, making her vanish
- // To prevent that, we'll never draw that part
- state.left += 50;
+ return true;
}
- if (_needBlit)
- _vm->_draw->forceBlit(true);
+ // Copy pixel-by-pixel
+ while (h-- > 0) {
+ const byte *srcRow = src;
+ byte *dstRow = dst;
- if (modifiedPal && (palCmd == 16)) {
- if (_backSurf)
- _vm->_draw->forceBlit();
- _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
- _vm->_draw->_noInvalidated = true;
- _vm->_video->dirtyRectsAll();
+ for (int32 i = 0; i < w; i++, srcRow++, dstRow++)
+ if (*srcRow != transp)
+ *dstRow = *srcRow;
+
+ src += surface->pitch;
+ dst += pitch;
}
- if ((state.flags & Graphics::CoktelVideo::kStatePalette) && (palCmd > 1)) {
- copyPalette(cVideo, palStart, palEnd);
+ return true;
+}
- if (!_backSurf)
- _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
- else
- _vm->_draw->_applyPal = true;
- }
+const VideoPlayer::Video *VideoPlayer::getVideoBySlot(int slot) const {
+ if ((slot < 0) || (slot >= kVideoSlotCount))
+ return 0;
- if (modifiedPal && (palCmd == 8) && _backSurf)
- _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ if (_videoSlots[slot].isEmpty())
+ return 0;
+ return &_videoSlots[slot];
+}
- if (!_ownSurf) {
- if (_backSurf) {
- _vm->_draw->invalidateRect(state.left, state.top, state.right, state.bottom);
- _vm->_draw->blitInvalidated();
- } else
- _vm->_video->dirtyRectsAdd(state.left, state.top, state.right, state.bottom);
+VideoPlayer::Video *VideoPlayer::getVideoBySlot(int slot) {
+ if ((slot < 0) || (slot >= kVideoSlotCount))
+ return 0;
- if (!noRetrace)
- _vm->_video->retrace();
- }
+ if (_videoSlots[slot].isEmpty())
+ return 0;
- // Subtitle
- if (state.flags & Graphics::CoktelVideo::kStateSpeech)
- _vm->_draw->printTotText(state.speechId);
+ return &_videoSlots[slot];
+}
- if (modifiedPal && ((palCmd == 2) || (palCmd == 4)))
- _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
+int VideoPlayer::getNextFreeSlot() {
+ // Starting with 1, since 0 is reserved for the "primary" video
+ for (int i = 1; i < kVideoSlotCount; i++)
+ if (_videoSlots[i].isEmpty())
+ return i;
+
+ return -1;
}
-bool VideoPlayer::doPlay(int16 frame, int16 breakKey,
- uint16 palCmd, int16 palStart, int16 palEnd,
- int16 palFrame, int16 endFrame, bool noRetrace) {
+void VideoPlayer::evalBgShading(Video &video) {
+ if (video.decoder->isSoundPlaying())
+ _vm->_sound->bgShade();
+ else
+ _vm->_sound->bgUnshade();
+}
- playFrame(frame, breakKey, palCmd, palStart, palEnd, palFrame, endFrame, noRetrace);
+Common::String VideoPlayer::findFile(const Common::String &file, Properties &properties) {
- _vm->_util->processInput();
+ bool hasExtension = false;
- if (_vm->shouldQuit()) {
- _primaryVideo->getVideo()->disableSound();
- return true;
- }
+ Common::String base = file;
+ Common::String fileName = file;
- if (breakKey != 0) {
- _vm->_util->getMouseState(&_vm->_global->_inter_mouseX,
- &_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons);
+ const char *posDot = strrchr(base.c_str(), '.');
+ if (posDot) {
+ hasExtension = true;
+ base = Common::String(base.c_str(), posDot);
+ posDot++;
+ }
- _vm->_inter->storeKey(_vm->_util->checkKey());
- if (VAR(0) == (unsigned) breakKey) {
- _primaryVideo->getVideo()->disableSound();
- // Seek to the last frame. Some scripts depend on that.
- _primaryVideo->getVideo()->seekFrame(endFrame, SEEK_SET, true);
- return true;
+ if (hasExtension) {
+ int i;
+ for (i = 0; i < ARRAYSIZE(_extensions); i++) {
+ if (!scumm_stricmp(posDot, _extensions[i])) {
+ if ((properties.type != kVideoTypeTry) && (properties.type == ((Type) i))) {
+ warning("Attempted to open video \"%s\", but requested a different type", fileName.c_str());
+ return "";
+ }
+ properties.type = (Type) i;
+ break;
+ }
}
+ if (i >= ARRAYSIZE(_extensions))
+ hasExtension = false;
}
- return false;
-}
+ if (!hasExtension) {
+ // No or unrecognized extension. Probing.
-void VideoPlayer::copyPalette(Graphics::CoktelVideo &video, int16 palStart, int16 palEnd) {
- if (!(video.getFeatures() & Graphics::CoktelVideo::kFeaturesPalette))
- return;
+ int i;
+ for (i = 0; i < ARRAYSIZE(_extensions); i++) {
+ if ((properties.type == kVideoTypeTry) || (properties.type == ((Type) i))) {
+ fileName = base + "." + _extensions[i];
- if (palStart < 0)
- palStart = 0;
- if (palEnd < 0)
- palEnd = 255;
+ if (_vm->_dataIO->existData(fileName.c_str())) {
+ properties.type = (Type) i;
+ break;
+ }
+ }
+ }
+ if ((i >= ARRAYSIZE(_extensions)) || (properties.type == kVideoTypeTry)) {
+ warning("Couldn't open video \"%s\"", file.c_str());
+ return "";
+ }
- memcpy(((char *)(_vm->_global->_pPaletteDesc->vgaPal)) + palStart * 3,
- video.getPalette() + palStart * 3,
- (palEnd - palStart + 1) * 3);
-}
+ }
-void VideoPlayer::writeVideoInfo(const char *videoFile, int16 varX, int16 varY,
- int16 varFrames, int16 varWidth, int16 varHeight) {
+ return fileName;
+}
- if (primaryOpen(videoFile)) {
- int16 x, y, width, height;
+Graphics::CoktelDecoder *VideoPlayer::openVideo(const Common::String &file, Properties &properties) {
+ Common::String fileName = findFile(file, properties);
+ if (fileName.empty())
+ return 0;
- x = _primaryVideo->getVideo()->getX();
- y = _primaryVideo->getVideo()->getY();
- width = _primaryVideo->getVideo()->getWidth();
- height = _primaryVideo->getVideo()->getHeight();
+ Common::SeekableReadStream *stream = _vm->_dataIO->getDataStream(fileName.c_str());
+ if (!stream)
+ return 0;
- if (VAR_OFFSET(varX) == 0xFFFFFFFF)
- _primaryVideo->getVideo()->getFrameCoords(1, x, y, width, height);
+ Graphics::CoktelDecoder *video = 0;
+ if (properties.type == kVideoTypeIMD)
+ video = new Graphics::IMDDecoder(_vm->_mixer, Audio::Mixer::kSFXSoundType);
+ else if (properties.type == kVideoTypePreIMD)
+ video = new Graphics::PreIMDDecoder(properties.width, properties.height, _vm->_mixer, Audio::Mixer::kSFXSoundType);
+ else if (properties.type == kVideoTypeVMD)
+ video = new Graphics::VMDDecoder(_vm->_mixer, Audio::Mixer::kSFXSoundType);
+ else if (properties.type == kVideoTypeRMD)
+ video = new Graphics::VMDDecoder(_vm->_mixer, Audio::Mixer::kSFXSoundType);
+ else
+ warning("Couldn't open video \"%s\": Invalid video Type", fileName.c_str());
- WRITE_VAR_OFFSET(varX, x);
- WRITE_VAR_OFFSET(varY, y);
- WRITE_VAR_OFFSET(varFrames, _primaryVideo->getVideo()->getFramesCount());
- WRITE_VAR_OFFSET(varWidth, width);
- WRITE_VAR_OFFSET(varHeight, height);
+ if (!video) {
+ delete stream;
+ return 0;
+ }
- primaryClose();
- } else {
- WRITE_VAR_OFFSET(varX, (uint32) -1);
- WRITE_VAR_OFFSET(varY, (uint32) -1);
- WRITE_VAR_OFFSET(varFrames, (uint32) -1);
- WRITE_VAR_OFFSET(varWidth, (uint32) -1);
- WRITE_VAR_OFFSET(varHeight, (uint32) -1);
+ if (!video->load(stream)) {
+ delete video;
+ return 0;
}
+
+ properties.width = video->getWidth();
+ properties.height = video->getHeight();
+
+ return video;
}
-void VideoPlayer::evalBgShading(Graphics::CoktelVideo &video) {
- if (video.isSoundPlaying())
- _vm->_sound->bgShade();
- else
- _vm->_sound->bgUnshade();
+void VideoPlayer::copyPalette(const Video &video, int16 palStart, int16 palEnd) {
+ if (!video.decoder->hasPalette())
+ return;
+
+ if (palStart < 0)
+ palStart = 0;
+ if (palEnd < 0)
+ palEnd = 255;
+
+ palStart = palStart * 3;
+ palEnd = (palEnd + 1) * 3;
+
+ for (int i = palStart; i <= palEnd; i++)
+ ((char *)(_vm->_global->_pPaletteDesc->vgaPal))[i] = video.decoder->getPalette()[i] >> 2;
}
} // End of namespace Gob
diff --git a/engines/gob/videoplayer.h b/engines/gob/videoplayer.h
index 8ca8aebf44..d91d0a3845 100644
--- a/engines/gob/videoplayer.h
+++ b/engines/gob/videoplayer.h
@@ -27,11 +27,15 @@
#define GOB_VIDEOPLAYER_H
#include "common/array.h"
+#include "common/list.h"
+#include "common/rect.h"
#include "common/str.h"
-#include "graphics/video/coktelvideo/coktelvideo.h"
+#include "graphics/surface.h"
+#include "graphics/video/coktel_decoder.h"
#include "gob/util.h"
+#include "gob/draw.h"
namespace Gob {
@@ -41,132 +45,135 @@ class DataStream;
class VideoPlayer {
public:
enum Flags {
- kFlagNone = 0,
- kFlagUseBackSurfaceContent = 0x40,
- kFlagFrontSurface = 0x80,
- kFlagNoVideo = 0x100,
- kFlagOtherSurface = 0x800,
- kFlagScreenSurface = 0x400000
+ kFlagNone = 0x000000,
+ kFlagUseBackSurfaceContent = 0x000040, ///< Use the back surface as a video "base".
+ kFlagFrontSurface = 0x000080, ///< Draw directly into the front surface.
+ kFlagNoVideo = 0x000100, ///< Only sound.
+ kFlagOtherSurface = 0x000800, ///< Draw into a specific sprite.
+ kFlagScreenSurface = 0x400000 ///< Draw into a newly created sprite of screen dimensions.
};
+ /** Video format. */
enum Type {
- kVideoTypeTry = -1,
- kVideoTypeIMD = 0,
- kVideoTypePreIMD = 1,
- kVideoTypeVMD = 2,
- kVideoTypeRMD = 3
+ kVideoTypeTry = -1, ///< Try any format.
+ kVideoTypeIMD = 0,
+ kVideoTypePreIMD = 1, ///< Early IMD format found in Fascination.
+ kVideoTypeVMD = 2,
+ kVideoTypeRMD = 3 ///< VMD containing "reversed" video.
};
- VideoPlayer(GobEngine *vm);
- ~VideoPlayer();
+ struct Properties {
+ Type type; ///< Type of the video to open.
- bool primaryOpen(const char *videoFile, int16 x = -1, int16 y = -1,
- int32 flags = kFlagFrontSurface, Type which = kVideoTypeTry,
- int16 width = -1, int16 height = -1);
- bool primaryPlay(int16 startFrame = -1, int16 lastFrame = -1,
- int16 breakKey = kShortKeyEscape,
- uint16 palCmd = 8, int16 palStart = 0, int16 palEnd = 255,
- int16 palFrame = -1, int16 endFrame = -1, bool fade = false,
- int16 reverseTo = -1, bool forceSeek = false);
- void primaryClose();
-
- void playFrame(int16 frame, int16 breakKey = kShortKeyEscape,
- uint16 palCmd = 8, int16 palStart = 0, int16 palEnd = 255,
- int16 palFrame = -1 , int16 endFrame = -1, bool noRetrace = false);
-
- int slotOpen(const char *videoFile, Type which = kVideoTypeTry,
- int16 width = -1, int16 height = -1);
- void slotPlay(int slot, int16 frame = -1);
- void slotClose(int slot);
- void slotCopyFrame(int slot, byte *dest,
- uint16 left, uint16 top, uint16 width, uint16 height,
- uint16 x, uint16 y, uint16 pitch, int16 transp = -1);
- void slotCopyPalette(int slot, int16 palStart = -1, int16 palEnd = -1);
- void slotWaitEndFrame(int slot = -1, bool onlySound = false);
+ int sprite; ///< The sprite onto which to draw the video.
- void slotSetDoubleMode(int slot, bool doubleMode);
+ int32 x; ///< X coordinate of the video.
+ int32 y; ///< Y coordinate of the video.
+ int32 width; ///< Width of the video.
+ int32 height; ///< Height of the video.
- bool slotIsOpen(int slot) const;
+ uint32 flags; ///< Video flags.
- const char *getFileName(int slot = -1) const;
- uint16 getFlags(int slot = -1) const;
- int16 getFramesCount(int slot = -1) const;
- int16 getCurrentFrame(int slot = -1) const;
- int16 getWidth(int slot = -1) const;
- int16 getHeight(int slot = -1) const;
- int16 getDefaultX(int slot = -1) const;
- int16 getDefaultY(int slot = -1) const;
+ int32 startFrame; ///< Frame to start playback from.
+ int32 lastFrame; ///< Frame to stop playback at.
+ int32 endFrame; ///< Last frame of this playback cycle.
- Graphics::CoktelVideo::State getState(int slot = -1) const;
- uint32 getFeatures(int slot = -1) const;
+ bool forceSeek; ///< Force the seeking to the start frame.
- bool hasExtraData(const char *fileName, int slot = -1) const;
- Common::MemoryReadStream *getExtraData(const char *fileName, int slot = -1);
+ int16 breakKey; ///< Keycode of the break/abort key.
- void writeVideoInfo(const char *videoFile, int16 varX, int16 varY,
- int16 varFrames, int16 varWidth, int16 varHeight);
+ uint16 palCmd; ///< Palette command.
+ int16 palStart; ///< Palette entry to start with.
+ int16 palEnd; ///< Palette entry to end at.
+ int32 palFrame; ///< Frame to apply the palette command at.
-private:
- class Video {
- public:
- Video(GobEngine *vm);
- ~Video();
+ bool fade; ///< Fade in?
+
+ bool waitEndFrame; ///< Wait for the frame's time to run out?
+
+ bool canceled; ///< Was the video canceled?
+
+ Properties();
+ };
+
+ VideoPlayer(GobEngine *vm);
+ ~VideoPlayer();
+
+ void evaluateFlags(Properties &properties);
+
+ int openVideo(bool primary, const Common::String &file, Properties &properties);
+ bool closeVideo(int slot = 0);
+
+ bool play(int slot, Properties &properties);
+ void waitEndFrame(int slot, bool onlySound = false);
- bool open(const char *fileName, Type which, int16 width, int16 height);
- void close();
+ bool slotIsOpen(int slot = 0) const;
- bool isOpen() const;
+ Common::String getFileName(int slot = 0) const;
- const char *getFileName() const;
- Graphics::CoktelVideo *getVideo();
- const Graphics::CoktelVideo *getVideo() const;
+ uint32 getFrameCount (int slot = 0) const;
+ uint32 getCurrentFrame(int slot = 0) const;
+ uint16 getWidth (int slot = 0) const;
+ uint16 getHeight (int slot = 0) const;
+ uint16 getDefaultX (int slot = 0) const;
+ uint16 getDefaultY (int slot = 0) const;
- Graphics::CoktelVideo::State getState() const;
- uint32 getFeatures() const;
+ const Common::List<Common::Rect> *getDirtyRects(int slot = 0) const;
- int16 getDefaultX() const;
- int16 getDefaultY() const;
+ bool hasEmbeddedFile(const Common::String &fileName, int slot = 0) const;
+ Common::MemoryReadStream *getEmbeddedFile(const Common::String &fileName, int slot = 0);
- bool hasExtraData(const char *fileName) const;
- Common::MemoryReadStream *getExtraData(const char *fileName);
+ int32 getSubtitleIndex(int slot = 0) const;
- Graphics::CoktelVideo::State nextFrame();
+ void writeVideoInfo(const Common::String &file, int16 varX, int16 varY,
+ int16 varFrames, int16 varWidth, int16 varHeight);
+
+ bool copyFrame(int slot, byte *dest,
+ uint16 left, uint16 top, uint16 width, uint16 height,
+ uint16 x, uint16 y, uint16 pitch, int16 transp = -1) const;
+
+private:
+ struct Video {
+ Graphics::CoktelDecoder *decoder;
+ Common::String fileName;
+
+ SurfaceDescPtr surface;
- private:
- GobEngine *_vm;
+ Video();
- Common::String _fileName;
- DataStream *_stream;
- Graphics::CoktelVideo *_video;
- Graphics::CoktelVideo::State _state;
- int16 _defaultX, _defaultY;
+ bool isEmpty() const;
+ void close();
};
+ static const int kVideoSlotCount = 32;
+
static const char *_extensions[];
GobEngine *_vm;
- Common::Array<Video *> _videoSlots;
- Video *_primaryVideo;
- bool _ownSurf;
- bool _backSurf;
+ // _videoSlots[0] is reserved for the "primary" video
+ Video _videoSlots[kVideoSlotCount];
+
bool _needBlit;
- bool _noCursorSwitch;
+ bool _noCursorSwitch;
bool _woodruffCohCottWorkaround;
- bool findFile(char *fileName, Type &which);
-
- const Video *getVideoBySlot(int slot = -1) const;
- Video *getVideoBySlot(int slot = -1);
+ const Video *getVideoBySlot(int slot) const;
+ Video *getVideoBySlot(int slot);
int getNextFreeSlot();
- void copyPalette(Graphics::CoktelVideo &video, int16 palStart = -1, int16 palEnd = -1);
- bool doPlay(int16 frame, int16 breakKey,
- uint16 palCmd, int16 palStart, int16 palEnd,
- int16 palFrame, int16 endFrame, bool noRetrace = false);
- void evalBgShading(Graphics::CoktelVideo &video);
+ Common::String findFile(const Common::String &file, Properties &properties);
+
+ Graphics::CoktelDecoder *openVideo(const Common::String &file, Properties &properties);
+
+ bool playFrame(int slot, Properties &properties);
+
+ void checkAbort(Video &video, Properties &properties);
+ void evalBgShading(Video &video);
+
+ void copyPalette(const Video &video, int16 palStart, int16 palEnd);
};
} // End of namespace Gob
diff --git a/engines/groovie/cell.h b/engines/groovie/cell.h
index 39ee529beb..a5feab4017 100644
--- a/engines/groovie/cell.h
+++ b/engines/groovie/cell.h
@@ -26,11 +26,7 @@
#ifndef GROOVIE_CELL_H
#define GROOVIE_CELL_H
-#include "common/file.h"
-#include "common/util.h"
-
-#include "groovie/cell.h"
-#include "groovie/groovie.h"
+#include "common/textconsole.h"
#define BOARDSIZE 7
#define CELL_CLEAR 0
diff --git a/engines/groovie/cursor.cpp b/engines/groovie/cursor.cpp
index 3f304c7859..2d0a2df245 100644
--- a/engines/groovie/cursor.cpp
+++ b/engines/groovie/cursor.cpp
@@ -403,18 +403,15 @@ GrvCursorMan_v2::GrvCursorMan_v2(OSystem *system) :
// Open the icons file
Common::File iconsFile;
- if (!iconsFile.open("icons.ph")) {
+ if (!iconsFile.open("icons.ph"))
error("Groovie::Cursor: Couldn't open icons.ph");
- return;
- }
// Verify the signature
- uint32 tmp32 = iconsFile.readUint32LE();
+ uint32 tmp32 = iconsFile.readUint32BE();
uint16 tmp16 = iconsFile.readUint16LE();
- if (tmp32 != 0x6e6f6369 || tmp16 != 1) {
- error("Groovie::Cursor: icons.ph signature failed: %04X %d", tmp32, tmp16);
- return;
- }
+ if (tmp32 != MKID_BE('icon') || tmp16 != 1)
+ error("Groovie::Cursor: icons.ph signature failed: %s %d", tag2str(tmp32), tmp16);
+
// Read the number of icons
uint16 nicons = iconsFile.readUint16LE();
diff --git a/engines/groovie/cursor.h b/engines/groovie/cursor.h
index 83aebb37d3..7a1f3ccc0e 100644
--- a/engines/groovie/cursor.h
+++ b/engines/groovie/cursor.h
@@ -26,9 +26,8 @@
#ifndef GROOVIE_CURSOR_H
#define GROOVIE_CURSOR_H
-#include "common/system.h"
#include "common/array.h"
-#include "common/file.h"
+#include "common/system.h"
namespace Common {
class MacResManager;
diff --git a/engines/groovie/debug.cpp b/engines/groovie/debug.cpp
index 41ebb2fbcd..7055965917 100644
--- a/engines/groovie/debug.cpp
+++ b/engines/groovie/debug.cpp
@@ -24,15 +24,17 @@
*/
#include "groovie/debug.h"
+#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "groovie/script.h"
#include "common/debug-channels.h"
+#include "common/system.h"
namespace Groovie {
Debugger::Debugger(GroovieEngine *vm) :
- _vm (vm), _script(_vm->_script), _syst(_vm->_system) {
+ _vm(vm), _script(_vm->_script) {
// Register the debugger comands
DCmd_Register("step", WRAP_METHOD(Debugger, cmd_step));
@@ -136,7 +138,7 @@ bool Debugger::cmd_playref(int argc, const char **argv) {
bool Debugger::cmd_dumppal(int argc, const char **argv) {
uint16 i;
byte palettedump[256 * 4];
- _syst->grabPalette(palettedump, 0, 256);
+ _vm->_system->grabPalette(palettedump, 0, 256);
for (i = 0; i < 256; i++) {
DebugPrintf("%3d: %3d,%3d,%3d,%3d\n", i, palettedump[(i * 4)], palettedump[(i * 4) + 1], palettedump[(i * 4) + 2], palettedump[(i * 4) + 3]);
diff --git a/engines/groovie/debug.h b/engines/groovie/debug.h
index dadba9482c..e21746a426 100644
--- a/engines/groovie/debug.h
+++ b/engines/groovie/debug.h
@@ -27,12 +27,11 @@
#define GROOVIE_DEBUG_H
#include "gui/debugger.h"
-#include "engines/engine.h"
namespace Groovie {
-class Script;
class GroovieEngine;
+class Script;
class Debugger : public GUI::Debugger {
public:
@@ -42,7 +41,6 @@ public:
private:
GroovieEngine *_vm;
Script *_script;
- OSystem *_syst;
int getNumber(const char *arg);
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index 5b0fa4b3b1..b30c2361d2 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -23,12 +23,12 @@
*
*/
-#include "common/savefile.h"
-
#include "groovie/groovie.h"
#include "groovie/detection.h"
#include "groovie/saveload.h"
+#include "common/system.h"
+
namespace Groovie {
static const PlainGameDescriptor groovieGames[] = {
@@ -176,7 +176,11 @@ static const ADParams detectionParams = {
// Flags
kADFlagUseExtraAsHint,
// Additional GUI options (for every game}
- Common::GUIO_NOSUBTITLES | Common::GUIO_NOSFX
+ Common::GUIO_NOSUBTITLES | Common::GUIO_NOSFX,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
diff --git a/engines/groovie/font.cpp b/engines/groovie/font.cpp
index ece8447735..dc1d7ae73a 100644
--- a/engines/groovie/font.cpp
+++ b/engines/groovie/font.cpp
@@ -23,106 +23,106 @@
*
*/
-#include "common/file.h"
-#include "graphics/surface.h"
-
#include "groovie/font.h"
namespace Groovie {
-Font::Font(OSystem *syst) :
- _syst(syst), _sphinxfnt(NULL) {
-
- Common::File fontfile;
- if (!fontfile.open("sphinx.fnt")) {
- error("Groovie::Font: Couldn't open sphinx.fnt");
- }
- uint16 fontfilesize = fontfile.size();
- _sphinxfnt = fontfile.readStream(fontfilesize);
- fontfile.close();
+T7GFont::T7GFont() : _maxHeight(0), _maxWidth(0), _glyphs(0) {
}
-Font::~Font() {
- delete _sphinxfnt;
+T7GFont::~T7GFont() {
+ delete[] _glyphs;
}
-void Font::printstring(const char *messagein) {
- uint16 totalwidth = 0, currxoffset, i;
+bool T7GFont::load(Common::SeekableReadStream &stream) {
+ // Read the mapping of characters to glyphs
+ if (stream.read(_mapChar2Glyph, 128) < 128) {
+ error("Groovie::T7GFont: Couldn't read the character to glyph map");
+ return false;
+ }
- char message[15];
- memset(message, 0, 15);
+ // Calculate the number of glyphs
+ byte numGlyphs = 0;
+ for (int i = 0; i < 128; i++)
+ if (_mapChar2Glyph[i] >= numGlyphs)
+ numGlyphs = _mapChar2Glyph[i] + 1;
+
+ // Read the glyph offsets
+ uint16 *glyphOffsets = new uint16[numGlyphs];
+ for (int i = 0; i < numGlyphs; i++)
+ glyphOffsets[i] = stream.readUint16LE();
+
+ if (stream.eos()) {
+ error("Groovie::T7GFont: Couldn't read the glyph offsets");
+ delete[] glyphOffsets;
+ return false;
+ }
- // Clear the top bar
- Common::Rect topbar(640, 80);
- Graphics::Surface *gamescreen;
- gamescreen = _syst->lockScreen();
- gamescreen->fillRect(topbar, 0);
- _syst->unlockScreen();
+ // Allocate the glyph data
+ delete[] _glyphs;
+ _glyphs = new Glyph[numGlyphs];
+
+ // Read the glyphs
+ _maxHeight = _maxWidth = 0;
+ for (int i = 0; (i < numGlyphs) && !stream.eos(); i++) {
+ // Verify we're at the expected stream position
+ if (stream.pos() != glyphOffsets[i]) {
+ error("Groovie::T7GFont: Glyph %d starts at %d but the current "
+ "offset is %d", i, glyphOffsets[i], stream.pos());
+ return false;
+ }
- for (i = 0; i < 14; i++) {
- char chartocopy = messagein[i];
- if (chartocopy <= 0x00 || chartocopy == 0x24) {
- break;
+ // Read the glyph information
+ Glyph *g = &_glyphs[i];
+ g->width = stream.readByte();
+ g->julia = stream.readByte();
+
+ // Read the pixels data into a dynamic array (we don't know its length)
+ Common::Array<byte> data;
+ data.reserve(300);
+ byte b = stream.readByte();
+ while (!stream.eos() && (b != 0xFF)) {
+ data.push_back(b);
+ b = stream.readByte();
}
- message[i] = chartocopy;
- }
- Common::rtrim(message);
- for (i = 0; i < strlen(message); i++) {
- totalwidth += letterwidth(message[i]);
- }
- currxoffset = (640 - totalwidth) / 2;
- char *currpos = message;
- while (*(currpos) != 0) {
- currxoffset += printletter(*(currpos++), currxoffset);
- }
-}
-uint16 Font::letteroffset(char letter) {
- uint16 offset;
- offset = letter;
- _sphinxfnt->seek(offset);
- offset = _sphinxfnt->readByte() * 2 + 128;
- _sphinxfnt->seek(offset);
- offset = _sphinxfnt->readUint16LE();
- return offset;
-}
+ // Verify the pixel data size
+ assert (data.size() % g->width == 0);
+ g->height = data.size() / g->width;
-uint8 Font::letterwidth(char letter) {
- uint16 offset = letteroffset(letter);
- _sphinxfnt->seek(offset);
- return _sphinxfnt->readByte();
-}
+ // Copy the pixel data into the definitive static array
+ g->pixels = new byte[data.size()];
+ memcpy(g->pixels, data.begin(), data.size());
-uint8 Font::letterheight(char letter) {
- uint16 offset, width, julia, data, counter = 0;
- offset = letteroffset(letter);
- _sphinxfnt->seek(offset);
- width = _sphinxfnt->readByte();
- julia = _sphinxfnt->readByte();
- data = _sphinxfnt->readByte();
- while (data != 0xFF) {
- data = _sphinxfnt->readByte();
- counter++;
+ // Update the max values
+ if (g->width > _maxWidth)
+ _maxWidth = g->width;
+ if (g->height > _maxHeight)
+ _maxHeight = g->height;
}
- if (counter % width != 0) assert("font file corrupt");
- return counter / width;
+
+ delete[] glyphOffsets;
+ return true;
}
+void T7GFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) const {
+ // We ignore the color, as the font is already colored
+ const Glyph *glyph = getGlyph(chr);
+ const byte *src = glyph->pixels;
+ byte *target = (byte *)dst->getBasePtr(x, y);
-uint8 Font::printletter(char letter, uint16 xoffset) {
- uint16 offset, width, height, julia;
- offset = letteroffset(letter);
- height = letterheight(letter);
- _sphinxfnt->seek(offset);
- width = _sphinxfnt->readByte();
- julia = _sphinxfnt->readByte();
+ for (int i = 0; i < glyph->height; i++) {
+ memcpy(target, src, glyph->width);
+ src += glyph->width;
+ target += dst->pitch;
+ }
+}
- byte *data = new byte[width * height];
- _sphinxfnt->read(data, width * height);
- _syst->copyRectToScreen(data, width, xoffset, 16, width, height);
- delete[] data;
+const T7GFont::Glyph *T7GFont::getGlyph(byte chr) const {
+ assert (chr < 128);
- return width;
+ byte numGlyph = _mapChar2Glyph[chr];
+ return &_glyphs[numGlyph];
}
} // End of Groovie namespace
diff --git a/engines/groovie/font.h b/engines/groovie/font.h
index 1a4a967fa6..71f8393d28 100644
--- a/engines/groovie/font.h
+++ b/engines/groovie/font.h
@@ -27,24 +27,38 @@
#define GROOVIE_FONT_H
#include "common/stream.h"
-#include "common/system.h"
+#include "graphics/font.h"
namespace Groovie {
-class Font {
+class T7GFont : public Graphics::Font {
public:
- Font(OSystem *syst);
- ~Font();
- void printstring(const char *messagein);
+ T7GFont();
+ ~T7GFont();
+
+ bool load(Common::SeekableReadStream &stream);
+
+ int getFontHeight() const { return _maxHeight; }
+ int getMaxCharWidth() const { return _maxWidth; }
+ int getCharWidth(byte chr) const { return getGlyph(chr)->width; }
+ void drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) const;
private:
- OSystem *_syst;
- Common::MemoryReadStream *_sphinxfnt;
+ int _maxHeight, _maxWidth;
+
+ struct Glyph {
+ Glyph() : pixels(0) {}
+ ~Glyph() { delete[] pixels; }
+
+ byte width;
+ byte height;
+ byte julia;
+ byte *pixels;
+ };
- uint16 letteroffset(char letter);
- uint8 letterwidth(char letter);
- uint8 letterheight(char letter);
- uint8 printletter(char letter, uint16 xoffset);
+ byte _mapChar2Glyph[128];
+ Glyph *_glyphs;
+ const Glyph *getGlyph(byte chr) const;
};
} // End of Groovie namespace
diff --git a/engines/groovie/graphics.cpp b/engines/groovie/graphics.cpp
index 1e54f0e79b..8546a13d40 100644
--- a/engines/groovie/graphics.cpp
+++ b/engines/groovie/graphics.cpp
@@ -23,8 +23,9 @@
*
*/
-#include "groovie/groovie.h"
#include "groovie/graphics.h"
+#include "groovie/groovie.h"
+#include "common/system.h"
namespace Groovie {
diff --git a/engines/groovie/graphics.h b/engines/groovie/graphics.h
index ea3261c85f..c9bade9538 100644
--- a/engines/groovie/graphics.h
+++ b/engines/groovie/graphics.h
@@ -26,6 +26,8 @@
#ifndef GROOVIE_GRAPHICS_H
#define GROOVIE_GRAPHICS_H
+#include "graphics/surface.h"
+
namespace Groovie {
class GroovieEngine;
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index ba18b37690..cdf5171ab9 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -23,27 +23,30 @@
*
*/
+#include "groovie/groovie.h"
+#include "groovie/cursor.h"
+#include "groovie/detection.h"
+#include "groovie/graphics.h"
+#include "groovie/music.h"
+#include "groovie/resource.h"
+#include "groovie/roq.h"
+#include "groovie/vdx.h"
+
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/macresman.h"
#include "engines/util.h"
-
+#include "graphics/fontman.h"
#include "sound/mixer.h"
-#include "groovie/groovie.h"
-#include "groovie/detection.h"
-#include "groovie/music.h"
-#include "groovie/roq.h"
-#include "groovie/vdx.h"
-
namespace Groovie {
GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) :
Engine(syst), _gameDescription(gd), _debugger(NULL), _script(NULL),
_resMan(NULL), _grvCursorMan(NULL), _videoPlayer(NULL), _musicPlayer(NULL),
- _graphicsMan(NULL), _macResFork(NULL), _waitingForInput(false) {
+ _graphicsMan(NULL), _macResFork(NULL), _waitingForInput(false), _font(NULL) {
// Adding the default directories
const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -104,12 +107,26 @@ Common::Error GroovieEngine::run() {
_graphicsMan = new GraphicsMan(this);
// Create the resource and cursor managers and the video player
+ // Prepare the font too
switch (_gameDescription->version) {
case kGroovieT7G:
- if (_gameDescription->desc.platform == Common::kPlatformMacintosh) {
+ if (getPlatform() == Common::kPlatformMacintosh) {
_macResFork = new Common::MacResManager();
if (!_macResFork->open(_gameDescription->desc.filesDescriptions[0].fileName))
error("Could not open %s as a resource fork", _gameDescription->desc.filesDescriptions[0].fileName);
+ // The Macintosh release used system fonts. We use GUI fonts.
+ _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+ } else {
+ Common::File fontfile;
+ if (!fontfile.open("sphinx.fnt")) {
+ error("Couldn't open sphinx.fnt");
+ return Common::kNoGameDataFoundError;
+ } else if (!_sphinxFont.load(fontfile)) {
+ error("Error loading sphinx.fnt");
+ return Common::kUnknownError;
+ }
+ fontfile.close();
+ _font = &_sphinxFont;
}
_resMan = new ResMan_t7g(_macResFork);
@@ -124,7 +141,7 @@ Common::Error GroovieEngine::run() {
}
// Create the music player
- if (_gameDescription->desc.platform == Common::kPlatformMacintosh)
+ if (getPlatform() == Common::kPlatformMacintosh)
_musicPlayer = new MusicPlayerMac(this);
else
_musicPlayer = new MusicPlayerXMI(this, _gameDescription->version == kGroovieT7G ? "fat" : "sample");
@@ -137,8 +154,8 @@ Common::Error GroovieEngine::run() {
if (_gameDescription->version == kGroovieT7G) {
// Run The 7th Guest's demo if requested
if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode"))
- filename = Common::String("demo.grv");
- else if (_gameDescription->desc.platform == Common::kPlatformMacintosh)
+ filename = "demo.grv";
+ else if (getPlatform() == Common::kPlatformMacintosh)
filename = "script.grv"; // Stored inside the executable's resource fork
} else if (_gameDescription->version == kGroovieV2) {
// Open the disk index
@@ -203,10 +220,8 @@ Common::Error GroovieEngine::run() {
_system->openCD(cd_num);
while (!shouldQuit()) {
- // Show the debugger if required
- if (_debugger->isAttached()) {
- _debugger->onFrame();
- }
+ // Give the debugger a chance to act
+ _debugger->onFrame();
// Handle input
Common::Event ev;
@@ -290,6 +305,10 @@ Common::Error GroovieEngine::run() {
return Common::kNoError;
}
+Common::Platform GroovieEngine::getPlatform() const {
+ return _gameDescription->desc.platform;
+}
+
bool GroovieEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsRTL) ||
diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h
index dae2df0595..8ae5f4157f 100644
--- a/engines/groovie/groovie.h
+++ b/engines/groovie/groovie.h
@@ -26,15 +26,11 @@
#ifndef GROOVIE_H
#define GROOVIE_H
-#include "engines/engine.h"
-#include "graphics/surface.h"
-
-#include "groovie/cursor.h"
#include "groovie/debug.h"
-#include "groovie/graphics.h"
-#include "groovie/player.h"
-#include "groovie/resource.h"
-#include "groovie/script.h"
+#include "groovie/font.h"
+
+#include "engines/engine.h"
+#include "graphics/pixelformat.h"
namespace Common {
class MacResManager;
@@ -57,7 +53,12 @@ namespace Common {
*/
namespace Groovie {
+class GraphicsMan;
+class GrvCursorMan;
class MusicPlayer;
+class ResMan;
+class Script;
+class VideoPlayer;
enum DebugLevels {
kGroovieDebugAll = 1 << 0,
@@ -81,6 +82,8 @@ public:
GroovieEngine(OSystem *syst, const GroovieGameDescription *gd);
~GroovieEngine();
+ Common::Platform getPlatform() const;
+
protected:
// Engine APIs
@@ -106,6 +109,7 @@ public:
VideoPlayer *_videoPlayer;
MusicPlayer *_musicPlayer;
GraphicsMan *_graphicsMan;
+ const Graphics::Font *_font;
Common::MacResManager *_macResFork;
@@ -113,6 +117,7 @@ private:
const GroovieGameDescription *_gameDescription;
Debugger *_debugger;
bool _waitingForInput;
+ T7GFont _sphinxFont;
};
} // End of namespace Groovie
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index f6670da716..6959a6a6f1 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -23,13 +23,14 @@
*
*/
-#include "groovie/lzss.h"
#include "groovie/music.h"
+#include "groovie/groovie.h"
#include "groovie/resource.h"
#include "common/config-manager.h"
#include "common/macresman.h"
#include "sound/audiocd.h"
+#include "sound/midiparser.h"
namespace Groovie {
@@ -110,6 +111,20 @@ void MusicPlayer::playCD(uint8 track) {
// Play the track starting at the requested offset (1000ms = 75 frames)
AudioCD.play(track - 1, 1, startms * 75 / 1000, 0);
+
+ // If the audio is not playing from the CD, play the "fallback" MIDI.
+ // The Mac version has no CD tracks, so it will always use the MIDI.
+ if (!AudioCD.isPlaying()) {
+ if (track == 2) {
+ // Intro MIDI fallback
+ if (_vm->getPlatform() == Common::kPlatformMacintosh)
+ playSong(70);
+ else
+ playSong((19 << 10) | 36); // XMI.GJD, file 36
+ } else if (track == 3) {
+ // TODO: Credits MIDI fallback
+ }
+ }
}
void MusicPlayer::startBackground() {
@@ -385,8 +400,8 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName)
_midiParser = MidiParser::createParser_XMIDI();
// Create the driver
- MidiDriverType driver = detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _driver = createMidi(driver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = createMidi(dev);
this->open();
// Set the parser's driver
@@ -401,9 +416,9 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName)
}
// Load the Global Timbre Library
- if (driver == MD_ADLIB) {
+ if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
// MIDI through AdLib
- _musicType = MD_ADLIB;
+ _musicType = MT_ADLIB;
loadTimbres(gtlName + ".ad");
// Setup the percussion channel
@@ -411,9 +426,9 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName)
if (_timbres[i].bank == 0x7F)
setTimbreAD(9, _timbres[i]);
}
- } else if ((driver == MD_MT32) || ConfMan.getBool("native_mt32")) {
+ } else if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")) {
// MT-32
- _musicType = MD_MT32;
+ _musicType = MT_MT32;
loadTimbres(gtlName + ".mt");
} else {
// GM
@@ -454,9 +469,9 @@ void MusicPlayerXMI::send(uint32 b) {
for (int i = 0; i < numTimbres; i++) {
if ((_timbres[i].bank == _chanBanks[chan]) &&
(_timbres[i].patch == patch)) {
- if (_musicType == MD_ADLIB) {
+ if (_musicType == MT_ADLIB) {
setTimbreAD(chan, _timbres[i]);
- } else if (_musicType == MD_MT32) {
+ } else if (_musicType == MT_MT32) {
setTimbreMT(chan, _timbres[i]);
}
return;
@@ -681,8 +696,8 @@ MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) {
_midiParser = MidiParser::createParser_SMF();
// Create the driver
- MidiDriverType driver = detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _driver = createMidi(driver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = createMidi(dev);
this->open();
// Set the parser's driver
@@ -702,20 +717,64 @@ bool MusicPlayerMac::load(uint32 fileref, bool loop) {
Common::SeekableReadStream *file = _vm->_macResFork->getResource(MKID_BE('cmid'), fileref & 0x3FF);
if (file) {
- // TODO: A form of LZSS, not supported by the current Groovie decoder.
- // The offset/length uint16 is BE, but not sure the amount of length bits
- // nor whether the offset is absolute/relative.
- warning("TODO: Mac Compressed MIDI: cmid 0x%04x", fileref & 0x3FF);
+ // Found the resource, decompress it
+ Common::SeekableReadStream *tmp = decompressMidi(file);
delete file;
- return false;
+ file = tmp;
+ } else {
+ // Otherwise, it's uncompressed
+ file = _vm->_macResFork->getResource(MKID_BE('Midi'), fileref & 0x3FF);
+ if (!file)
+ error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
}
- // Otherwise, it's uncompressed
- file = _vm->_macResFork->getResource(MKID_BE('Midi'), fileref & 0x3FF);
- if (!file)
- error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
-
return loadParser(file, loop);
}
+Common::SeekableReadStream *MusicPlayerMac::decompressMidi(Common::SeekableReadStream *stream) {
+ // Initialize an output buffer of the given size
+ uint32 size = stream->readUint32BE();
+ byte *output = (byte *)malloc(size);
+
+ byte *current = output;
+ uint32 decompBytes = 0;
+ while ((decompBytes < size) && !stream->eos()) {
+ // 8 flags
+ byte flags = stream->readByte();
+
+ for (byte i = 0; (i < 8) && !stream->eos(); i++) {
+ if (flags & 1) {
+ // 1: Next byte is a literal
+ *(current++) = stream->readByte();
+ if (stream->eos())
+ continue;
+ decompBytes++;
+ } else {
+ // 0: It's a reference to part of the history
+ uint16 args = stream->readUint16BE();
+ if (stream->eos())
+ continue;
+
+ // Length = 4bit unsigned (3 minimal)
+ uint8 length = (args >> 12) + 3;
+
+ // Offset = 12bit signed (all values are negative)
+ int16 offset = (args & 0xFFF) | 0xF000;
+
+ // Copy from the past decompressed bytes
+ decompBytes += length;
+ while (length > 0) {
+ *(current) = *(current + offset);
+ current++;
+ length--;
+ }
+ }
+ flags = flags >> 1;
+ }
+ }
+
+ // Return the output buffer wrapped in a MemoryReadStream
+ return new Common::MemoryReadStream(output, size, DisposeAfterUse::YES);
+}
+
} // End of Groovie namespace
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
index 6302c81dcc..5b5f5bd346 100644
--- a/engines/groovie/music.h
+++ b/engines/groovie/music.h
@@ -26,14 +26,16 @@
#ifndef GROOVIE_MUSIC_H
#define GROOVIE_MUSIC_H
-#include "groovie/groovie.h"
-
-#include "sound/mididrv.h"
-#include "sound/midiparser.h"
+#include "common/array.h"
#include "common/mutex.h"
+#include "sound/mididrv.h"
+
+class MidiParser;
namespace Groovie {
+class GroovieEngine;
+
class MusicPlayer {
public:
MusicPlayer(GroovieEngine *vm);
@@ -159,6 +161,9 @@ public:
protected:
bool load(uint32 fileref, bool loop);
+
+private:
+ Common::SeekableReadStream *decompressMidi(Common::SeekableReadStream *stream);
};
} // End of Groovie namespace
diff --git a/engines/groovie/player.cpp b/engines/groovie/player.cpp
index 5bac190701..8badd90012 100644
--- a/engines/groovie/player.cpp
+++ b/engines/groovie/player.cpp
@@ -23,8 +23,8 @@
*
*/
-#include "groovie/groovie.h"
#include "groovie/player.h"
+#include "groovie/groovie.h"
namespace Groovie {
diff --git a/engines/groovie/resource.cpp b/engines/groovie/resource.cpp
index 42de1a804d..32cc1735ef 100644
--- a/engines/groovie/resource.cpp
+++ b/engines/groovie/resource.cpp
@@ -26,8 +26,8 @@
#include "common/archive.h"
#include "common/macresman.h"
-#include "groovie/groovie.h"
#include "groovie/resource.h"
+#include "groovie/groovie.h"
namespace Groovie {
diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
index de91bb2067..11bacef8e8 100644
--- a/engines/groovie/roq.cpp
+++ b/engines/groovie/roq.cpp
@@ -26,8 +26,9 @@
// ROQ video player based on this specification by Dr. Tim Ferguson:
// http://www.csse.monash.edu.au/~timf/videocodec/idroq.txt
-#include "groovie/groovie.h"
#include "groovie/roq.h"
+#include "groovie/graphics.h"
+#include "groovie/groovie.h"
#include "graphics/jpeg.h"
diff --git a/engines/groovie/script.cpp b/engines/groovie/script.cpp
index 15a0a473c0..9fd7fa7d63 100644
--- a/engines/groovie/script.cpp
+++ b/engines/groovie/script.cpp
@@ -23,18 +23,19 @@
*
*/
-#include "groovie/debug.h"
-#include "groovie/music.h"
#include "groovie/script.h"
-#include "groovie/groovie.h"
#include "groovie/cell.h"
+#include "groovie/cursor.h"
+#include "groovie/graphics.h"
+#include "groovie/groovie.h"
+#include "groovie/music.h"
+#include "groovie/player.h"
+#include "groovie/resource.h"
#include "groovie/saveload.h"
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
-#include "common/endian.h"
-#include "common/events.h"
#include "common/EventRecorder.h"
#include "common/macresman.h"
@@ -63,9 +64,8 @@ static void debugScript(int level, bool nl, const char *s, ...) {
}
Script::Script(GroovieEngine *vm, EngineVersion version) :
- _code(NULL), _savedCode(NULL), _stacktop(0),
- _debugger(NULL), _vm(vm),
- _videoFile(NULL), _videoRef(0), _font(NULL), _staufsMove(NULL) {
+ _code(NULL), _savedCode(NULL), _stacktop(0), _debugger(NULL), _vm(vm),
+ _videoFile(NULL), _videoRef(0), _staufsMove(NULL) {
// Initialize the opcode set depending on the engine version
switch (version) {
case kGroovieT7G:
@@ -86,11 +86,11 @@ Script::Script(GroovieEngine *vm, EngineVersion version) :
}
// Initialize the music type variable
- int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- if (midiDriver == MD_ADLIB) {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
// MIDI through AdLib
setVariable(0x100, 0);
- } else if ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")) {
+ } else if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")) {
// MT-32
setVariable(0x100, 2);
} else {
@@ -112,7 +112,6 @@ Script::~Script() {
delete[] _code;
delete[] _savedCode;
- delete _font;
delete _videoFile;
}
@@ -429,6 +428,22 @@ void Script::savegame(uint slot) {
_saveNames[slot] = save;
}
+void Script::printString(Graphics::Surface *surface, const char *str) {
+ char message[15];
+ memset(message, 0, 15);
+
+ // Preprocess the string
+ for (int i = 0; i < 14; i++) {
+ if (str[i] <= 0x00 || str[i] == 0x24)
+ break;
+ message[i] = str[i];
+ }
+ Common::rtrim(message);
+
+ // Draw the string
+ _vm->_font->drawString(surface, message, 0, 16, 640, 0xE2, Graphics::kTextAlignCenter);
+}
+
// OPCODES
void Script::o_invalid() {
@@ -1249,11 +1264,16 @@ void Script::o_printstring() {
stringstorage[counter] = 0;
- // Load the font if required
- if (!_font) {
- _font = new Font(_vm->_system);
- }
- _font->printstring(stringstorage);
+ Common::Rect topbar(640, 80);
+ Graphics::Surface *gamescreen = _vm->_system->lockScreen();
+
+ // Clear the top bar
+ gamescreen->fillRect(topbar, 0);
+
+ // Draw the string
+ printString(gamescreen, stringstorage);
+
+ _vm->_system->unlockScreen();
}
void Script::o_hotspot_slot() {
@@ -1273,11 +1293,15 @@ void Script::o_hotspot_slot() {
return;
}
- // Load the font if required
- if (!_font) {
- _font = new Font(_vm->_system);
- }
- _font->printstring(_saveNames[slot].c_str());
+ Common::Rect topbar(640, 80);
+ Graphics::Surface *gamescreen = _vm->_system->lockScreen();
+
+ // Clear the top bar
+ gamescreen->fillRect(topbar, 0);
+
+ printString(gamescreen, _saveNames[slot].c_str());
+
+ _vm->_system->unlockScreen();
// Save the currently highlighted slot
_hotspotSlot = slot;
diff --git a/engines/groovie/script.h b/engines/groovie/script.h
index e4a6a288e6..cda87a8917 100644
--- a/engines/groovie/script.h
+++ b/engines/groovie/script.h
@@ -26,12 +26,16 @@
#ifndef GROOVIE_SCRIPT_H
#define GROOVIE_SCRIPT_H
-#include "common/file.h"
#include "common/random.h"
#include "common/rect.h"
-#include "groovie/font.h"
-#include "groovie/cell.h"
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+struct Surface;
+}
namespace Groovie {
@@ -40,8 +44,9 @@ enum EngineVersion {
kGroovieV2
};
-class GroovieEngine;
class CellGame;
+class Debugger;
+class GroovieEngine;
class Script {
friend class Debugger;
@@ -112,7 +117,6 @@ private:
uint16 _hotspotSlot;
// Video
- Font *_font;
Common::SeekableReadStream *_videoFile;
uint32 _videoRef;
uint16 _bitflags;
@@ -140,6 +144,7 @@ private:
void loadgame(uint slot);
void savegame(uint slot);
bool playvideofromref(uint32 fileref);
+ void printString(Graphics::Surface *surface, const char *str);
// Opcodes
typedef void (Script::*OpcodeFunc)();
diff --git a/engines/groovie/vdx.cpp b/engines/groovie/vdx.cpp
index fb6377349f..61756cb218 100644
--- a/engines/groovie/vdx.cpp
+++ b/engines/groovie/vdx.cpp
@@ -23,9 +23,10 @@
*
*/
+#include "groovie/vdx.h"
+#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "groovie/lzss.h"
-#include "groovie/vdx.h"
#include "common/debug-channels.h"
#include "sound/mixer.h"
@@ -208,17 +209,14 @@ static const uint16 vdxBlockMapLookup[] = {
};
void VDXPlayer::getDelta(Common::ReadStream *in) {
- uint16 j, k, l;
- uint32 offset;
- uint8 currOpCode, param1, param2, param3;
+ uint16 k, l;
// Get the size of the local palette
- j = in->readUint16LE();
+ uint16 palSize = in->readUint16LE();
// Load the palette if it isn't empty
- if (j) {
+ if (palSize) {
uint16 palBitField[16];
- int flag = 1, palIndex;
// Load the bit field
for (l = 0; l < 16; l++) {
@@ -227,9 +225,9 @@ void VDXPlayer::getDelta(Common::ReadStream *in) {
// Load the actual palette
for (l = 0; l < 16; l++) {
- flag = 1 << 15;
- for (j = 0; j < 16; j++) {
- palIndex = (l * 16) + j;
+ int flag = 1 << 15;
+ for (uint16 j = 0; j < 16; j++) {
+ int palIndex = (l * 16) + j;
if (flag & palBitField[l]) {
for (k = 0; k < 3; k++) {
@@ -246,11 +244,12 @@ void VDXPlayer::getDelta(Common::ReadStream *in) {
setPalette(_palBuf);
}
}
- currOpCode = in->readByte();
- /* j now becomes the current block line we're dealing with */
- j = 0;
- offset = 0;
+ uint8 currOpCode = in->readByte();
+ uint8 param1, param2, param3;
+
+ uint16 currentLine = 0;
+ uint32 offset = 0;
while (!in->eos()) {
byte colours[16];
if (currOpCode < 0x60) {
@@ -276,8 +275,8 @@ void VDXPlayer::getDelta(Common::ReadStream *in) {
break;
case 0x61: /* Skip to the end of this line, next block is start of next */
/* Note this is used at the end of EVERY line */
- j++;
- offset = j * TILE_SIZE * 640;
+ currentLine++;
+ offset = currentLine * TILE_SIZE * 640;
break;
case 0x62:
case 0x63:
@@ -381,12 +380,15 @@ void VDXPlayer::getStill(Common::ReadStream *in) {
byte colours[16];
for (uint16 j = 0; j < numYTiles; j++) {
- for (uint16 i = 0; i < numXTiles; i++) { /* Tile number */
+ byte *currentTile = buf + j * TILE_SIZE * imageWidth;
+ for (uint16 i = numXTiles; i; i--) {
uint8 colour1 = in->readByte();
uint8 colour0 = in->readByte();
uint16 colourMap = in->readUint16LE();
expandColourMap(colours, colourMap, colour1, colour0);
- decodeBlockStill(buf + j * TILE_SIZE * imageWidth + i * TILE_SIZE, colours, 640, mask);
+ decodeBlockStill(currentTile, colours, 640, mask);
+
+ currentTile += TILE_SIZE;
}
}
@@ -424,20 +426,27 @@ void VDXPlayer::getStill(Common::ReadStream *in) {
}
void VDXPlayer::expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0) {
- int flag = 1 << 15;
- for (int i = 0; i < 16; i++) {
+ // It's a bit faster to start from the end
+ out += 16;
+ for (int i = 16; i; i--) {
// Set the corresponding colour
- out[i] = (colourMap & flag) ? colour1 : colour0;
+ // The following is an optimized version of:
+ // *--out = (colourMap & 1) ? colour1 : colour0;
+ uint8 selector = -(colourMap & 1);
+ *--out = (selector & colour1) | (~selector & colour0);
- // Update the flag to test the next colour
- flag >>= 1;
+ // Update the flag map to test the next colour
+ colourMap >>= 1;
}
}
void VDXPlayer::decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask) {
- for (int y = 0; y < TILE_SIZE; y++) {
- for (int x = 0; x < TILE_SIZE; x++) {
- if (_flagOne) {
+ assert(TILE_SIZE == 4);
+
+ for (int y = TILE_SIZE; y; y--) {
+ if (_flagOne) {
+ // TODO: optimize with bit logic?
+ for (int x = 0; x < TILE_SIZE; x++) {
// 0xff pixels don't modify the buffer
if (*colours != 0xff) {
// Write the colour
@@ -445,25 +454,28 @@ void VDXPlayer::decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, ui
// Note: if the mask is 0, it paints the image
// else, it paints the image's mask using 0xff
}
- } else {
- *buf = *colours;
+
+ // Point to the next colour
+ colours++;
+
+ // Point to the next pixel
+ buf++;
}
- // Point to the next colour
- colours++;
+ // Point to the start of the next line
+ buf += imageWidth - TILE_SIZE;
+ } else {
+ *((uint32 *)buf) = *((uint32 *)colours);
+ colours += 4;
- // Point to the next pixel
- buf++;
+ // Point to the start of the next line
+ buf += imageWidth;
}
-
- // Point to the start of the next line
- buf += imageWidth - TILE_SIZE;
}
}
void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth) {
- byte *fgBuf = (byte *)_fg->getBasePtr(0, 0) + offset;
- //byte *bgBuf = (byte *)_bg->getBasePtr(0, 0) + offset;
+ assert(TILE_SIZE == 4);
byte *dest;
// TODO: Verify just the else block is required
@@ -474,27 +486,38 @@ void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth
dest = (byte *)_bg->getBasePtr(0, 0) + offset;
//}
- int32 off = _origX + _origY * imageWidth;
- for (int y = 0; y < TILE_SIZE; y++) {
- for (int x = 0; x < TILE_SIZE; x++) {
- if (_flagSeven) {
- if (fgBuf[off] != 0xff) {
+ // Move the pointers to the beginning of the current block
+ int32 blockOff = _origX + _origY * imageWidth;
+ dest += blockOff;
+ byte *fgBuf = 0;
+ if (_flagSeven) {
+ fgBuf = (byte *)_fg->getBasePtr(0, 0) + offset + blockOff;
+ //byte *bgBuf = (byte *)_bg->getBasePtr(0, 0) + offset + blockOff;
+ }
+
+ for (int y = TILE_SIZE; y; y--) {
+ if (_flagSeven) {
+ // Paint mask
+ for (int x = 0; x < TILE_SIZE; x++) {
+ // TODO: this can probably be optimized with bit logic
+ if (fgBuf[x] != 0xff) {
if (*colours == 0xff) {
- dest[off] = fgBuf[off];
+ dest[x] = fgBuf[x];
} else {
- dest[off] = *colours;
+ dest[x] = *colours;
}
}
- } else {
- // Paint directly
- dest[off] = *colours;
+ colours++;
}
- colours++;
- off++;
+ fgBuf += imageWidth;
+ } else {
+ // Paint directly
+ *((uint32 *)dest) = *((uint32 *)colours);
+ colours += 4;
}
- // Prepare the offset of the next line
- off += imageWidth - TILE_SIZE;
+ // Move to the next line
+ dest += imageWidth;
}
}
diff --git a/engines/groovie/vdx.h b/engines/groovie/vdx.h
index 207d2e0c18..0b29493108 100644
--- a/engines/groovie/vdx.h
+++ b/engines/groovie/vdx.h
@@ -28,6 +28,10 @@
#include "groovie/player.h"
+namespace Common {
+ class ReadStream;
+}
+
namespace Groovie {
class VDXPlayer : public VideoPlayer {
diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp
index d71f7b8b25..225b44b3f4 100644
--- a/engines/kyra/debugger.cpp
+++ b/engines/kyra/debugger.cpp
@@ -240,7 +240,7 @@ bool Debugger_LoK::cmd_enterRoom(int argc, const char **argv) {
while (!_vm->_screen->isMouseVisible())
_vm->_screen->showMouse();
- _detach_now = true;
+ detach();
return false;
}
@@ -327,7 +327,7 @@ bool Debugger_v2::cmd_enterScene(int argc, const char **argv) {
while (!_vm->screen_v2()->isMouseVisible())
_vm->screen_v2()->showMouse();
- _detach_now = true;
+ detach();
return false;
}
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 6e9359e7fc..135a9ae7b2 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -41,1172 +41,7 @@ struct KYRAGameDescription {
Kyra::GameFlags flags;
};
-namespace {
-
-#define FLAGS(x, y, z, a, b, c, d, id) { Common::UNK_LANG, Common::UNK_LANG, Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
-#define FLAGS_FAN(fanLang, repLang, x, y, z, a, b, c, d, id) { Common::UNK_LANG, fanLang, repLang, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
-
-#define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA1)
-#define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_CD_FLAGS FLAGS(false, true, true, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_DEMO_CD_FLAGS FLAGS(true, true, true, false, false, false, false, Kyra::GI_KYRA1)
-
-#define KYRA2_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA2)
-#define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_TOWNS_SJIS_FLAGS FLAGS(false, false, false, true, false, false, false, Kyra::GI_KYRA2)
-
-#define KYRA3_CD_FLAGS FLAGS(false, false, true, false, false, true, true, Kyra::GI_KYRA3)
-#define KYRA3_CD_INS_FLAGS FLAGS(false, false, true, false, false, true, false, Kyra::GI_KYRA3)
-#define KYRA3_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, true, false, Kyra::GI_KYRA3)
-
-#define LOL_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_LOL)
-#define LOL_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_LOL)
-#define LOL_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_LOL)
-#define LOL_PC98_SJIS_FLAGS FLAGS(false, false, false, true, true, false, false, Kyra::GI_LOL)
-#define LOL_DEMO_FLAGS FLAGS(true, true, false, false, false, false, false, Kyra::GI_LOL)
-#define LOL_KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
-
-const KYRAGameDescription adGameDescs[] = {
- /* disable these targets until they get supported
- {
- {
- "kyra1",
- 0,
- AD_ENTRY1("DISK1.EXE", "c8641d0414d6c966d0a3dad79db07bf4"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_CMP_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- AD_ENTRY1("DISK1.EXE", "5d5cee4c3d0b68d586788b74243d254a"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_CMP_FLAGS
- },
- */
-
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "3c244298395520bb62b5edfe41688879"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "796e44863dd22fa635b042df1bf16673"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "abf8eb360e79a6c2a837751fbd4d3d24"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "6018e1dfeaca7fe83f8d0b00eb0dd049"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // from Arne.F
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "f0b276781f47c130f423ec9679fe9ed9"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // from VooD
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "8909b41596913b3f5deaf3c9f1017b01"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // floppy 1.8 from clemmy
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "747861d2a9c643c59fdab570df5b9093"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // from gourry
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "ef08c8c237ee1473fd52578303fc36df"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- {
- { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
- { "GEMCUT.EMC", 0, "2a3f44e179f1e9f7643e90083c747571", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_AMIGA_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- {
- { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
- { "GEMCUT.EMC", 0, "74f99e9ed99abf8d0429826d78485a2a", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_AMIGA_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- {
- { "GEMCUT.EMC", 0, "796e44863dd22fa635b042df1bf16673", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
-
- { // FM-TOWNS version
- {
- "kyra1",
- 0,
- {
- { "EMC.PAK", 0, "a046bb0b422061aab8e4c4689400343a", -1 },
- { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_TOWNS_FLAGS
- },
- {
- {
- "kyra1",
- 0,
- {
- { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
- { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::JA_JPN,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_TOWNS_SJIS_FLAGS
- },
-
- // PC-9801 floppy + CD / PC-9821 floppy version are all using the same data files,
- // thus we will mark it as non CD game.
- {
- {
- "kyra1",
- "",
- {
- { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
- { "MUSIC98.PAK", 0, "02fc212f799331b769b274e33d87b37f", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::JA_JPN,
- Common::kPlatformPC98,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_TOWNS_SJIS_FLAGS
- },
-
- {
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "fac399fe62f98671e56a005c5e94e39f"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "230f54e6afc007ab4117159181a1c722"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "b037c41768b652a040360ffa3556fd2a"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
-
- { // italian fan translation see fr#1727941 "KYRA: add Italian CD Version to kyra.dat"
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "d8327fc4b7a72b23c900fa13aef4093a"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
-
- { // Kyra 1 Mac CD as mentioned in fr #2766454 "KYRA1: Add support for Macintosh CD" by nnooiissee
- {
- "kyra1",
- "CD",
- {
- { "GEMCUT.PAK", 0, "d3d4b281cd357230aabcec46843d04bd", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- {
- { "GEMCUT.PAK", 0, "4a0cb720e824295bcbccbd1407652110", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformMacintosh,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- {
- { "GEMCUT.PAK", 0, "b71ee090aa12e80ed2ba068826d92bed", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformMacintosh,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
-
- {
- {
- "kyra1",
- "Demo",
- AD_ENTRY1("DEMO1.WSA", "fb722947d94897512b13b50cc84fd648"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NOSPEECH
- },
- KYRA1_DEMO_FLAGS
- },
-
- { // Special Kyrandia 1 CD demo
- {
- "kyra1",
- "Demo/CD",
- AD_ENTRY1("INTRO.VRM", "e3045fb69b8c29db84b8fda3ccbdac54"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_DEMO_CD_FLAGS
- },
-
- { // Floppy version
- {
- "kyra2",
- 0,
- AD_ENTRY1("WESTWOOD.001", "3f52dda68c4f7696c8309038be9f4151"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_CMP_FLAGS
- },
-
- { // Floppy version
- {
- "kyra2",
- 0,
- AD_ENTRY1("WESTWOOD.001", "d787b9559afddfe058b84c0b3a787224"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_CMP_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "1ba18be685ad8e5a0ab5d46a0ce4d345"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "262fb69dd8e52e596c7aefc6456f7c1b"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "f7de11506b4c8fdf64bc763206c3e4e7"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "e0a70c31b022cb4bb3061890020fc27c"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // CD version
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FLAGS
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FLAGS
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FLAGS
- },
-
- // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
- { // CD version
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- { // Interactive Demo
- {
- "kyra2",
- "CD/Demo",
- AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_CD_DEMO_FLAGS
- },
-
- { // Interactive Demo
- {
- "kyra2",
- "CD/Demo",
- AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_CD_DEMO_FLAGS
- },
-
- { // Interactive Demo
- {
- "kyra2",
- "CD/Demo",
- AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_CD_DEMO_FLAGS
- },
-
- { // Non-Interactive Demos
- {
- "kyra2",
- "Demo",
- AD_ENTRY1("VOC.PAK", "ecb3561b63749158172bf21528cf5f45"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_DEMO_FLAGS
- },
-
- { // FM-TOWNS
- {
- "kyra2",
- 0,
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::EN_ANY,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_FLAGS
- },
- {
- {
- "kyra2",
- 0,
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::JA_JPN,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_SJIS_FLAGS
- },
- { // PC-9821
- {
- "kyra2",
- "CD",
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::EN_ANY,
- Common::kPlatformPC98,
- ADGF_CD,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_FLAGS
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::JA_JPN,
- Common::kPlatformPC98,
- ADGF_CD,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_SJIS_FLAGS
- },
-
- // Kyra3
-
- // non installed version
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "WESTWOOD.001", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "WESTWOOD.001", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "WESTWOOD.001", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FLAGS
- },
-
- // installed version
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
-
- // Mac version
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformMacintosh,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformMacintosh,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
-
- // Spanish fan translation, see fr#1994040 "KYRA3: Add support for Spanish fan translation"
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
- },
-
- // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
- },
-
-#ifdef ENABLE_LOL
- // Lands of Lore CD
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- 0,
- {
- { "WESTWOOD.1", 0, "c656aa9a2b4032d341e3dc8e3525b917", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_CMP_FLAGS
- },
-
- {
- {
- "lol",
- 0,
- {
- { "WESTWOOD.1", 0, "3c61cb7de5b2ec452f5851f5075207ee", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_CMP_FLAGS
- },
-
- {
- {
- "lol",
- "Extracted",
- {
- { "GENERAL.PAK", 0, "2aaa30e120c08af87196820e9dd4bf73", -1 },
- { "CHAPTER7.PAK", 0, "eb92bf7ebb4e890add1233a6b0c810ff", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_FLAGS
- },
-
- {
- {
- "lol",
- "Extracted",
- {
- { "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 },
- { "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_FLAGS
- },
-
- {
- {
- "lol",
- 0,
- {
- { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 },
- { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 },
- { 0, 0, 0, 0 }
- },
- Common::JA_JPN,
- Common::kPlatformPC98,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_PC98_SJIS_FLAGS
- },
-
- {
- {
- "lol",
- "Demo",
- {
- { "INTRO.PAK", 0, "4bc22a3b57f19a49212c5de58ab014d6", -1 },
- { "INTROVOC.PAK", 0, "7e578e4f1da31c1f294e14a8e8f3cc44", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- LOL_DEMO_FLAGS
- },
-
- {
- {
- "lol",
- "Demo",
- {
- { "GENERAL.PAK", 0, "e94863d86c4597a2d581d05481c152ba", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NOSPEECH
- },
- LOL_KYRA2_DEMO_FLAGS
- },
-#endif // ENABLE_LOL
-
- { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0, 0) }
-};
-
-const PlainGameDescriptor gameList[] = {
- { "kyra1", "The Legend of Kyrandia" },
- { "kyra2", "The Legend of Kyrandia: The Hand of Fate" },
- { "kyra3", "The Legend of Kyrandia: Malcolm's Revenge" },
-#ifdef ENABLE_LOL
- { "lol", "Lands of Lore: The Throne of Chaos" },
-#endif // ENABLE_LOL
- { 0, 0 }
-};
+#include "kyra/detection_tables.h"
const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -1226,11 +61,13 @@ const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
-} // End of anonymous namespace
-
class KyraMetaEngine : public AdvancedMetaEngine {
public:
KyraMetaEngine() : AdvancedMetaEngine(detectionParams) {}
@@ -1268,7 +105,8 @@ bool Kyra::KyraEngine_v1::hasFeature(EngineFeature f) const {
return
(f == kSupportsRTL) ||
(f == kSupportsLoadingDuringRuntime) ||
- (f == kSupportsSavingDuringRuntime);
+ (f == kSupportsSavingDuringRuntime) ||
+ (f == kSupportsSubtitleOptions);
}
bool KyraMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
new file mode 100644
index 0000000000..8eabb15264
--- /dev/null
+++ b/engines/kyra/detection_tables.h
@@ -0,0 +1,1209 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+namespace {
+
+#define FLAGS(x, y, z, a, b, c, d, id) { Common::UNK_LANG, Common::UNK_LANG, Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
+#define FLAGS_FAN(fanLang, repLang, x, y, z, a, b, c, d, id) { Common::UNK_LANG, fanLang, repLang, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
+
+#define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA1)
+#define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_CD_FLAGS FLAGS(false, true, true, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_DEMO_CD_FLAGS FLAGS(true, true, true, false, false, false, false, Kyra::GI_KYRA1)
+
+#define KYRA2_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA2)
+#define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_TOWNS_SJIS_FLAGS FLAGS(false, false, false, true, false, false, false, Kyra::GI_KYRA2)
+
+#define KYRA3_CD_FLAGS FLAGS(false, false, true, false, false, true, true, Kyra::GI_KYRA3)
+#define KYRA3_CD_INS_FLAGS FLAGS(false, false, true, false, false, true, false, Kyra::GI_KYRA3)
+#define KYRA3_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, true, false, Kyra::GI_KYRA3)
+
+#define LOL_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_LOL)
+#define LOL_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_LOL)
+#define LOL_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_LOL)
+#define LOL_PC98_SJIS_FLAGS FLAGS(false, false, false, true, true, false, false, Kyra::GI_LOL)
+#define LOL_DEMO_FLAGS FLAGS(true, true, false, false, false, false, false, Kyra::GI_LOL)
+#define LOL_KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
+
+const KYRAGameDescription adGameDescs[] = {
+ /* disable these targets until they get supported
+ {
+ {
+ "kyra1",
+ 0,
+ AD_ENTRY1("DISK1.EXE", "c8641d0414d6c966d0a3dad79db07bf4"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_CMP_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ AD_ENTRY1("DISK1.EXE", "5d5cee4c3d0b68d586788b74243d254a"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_CMP_FLAGS
+ },
+ */
+
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "3c244298395520bb62b5edfe41688879"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "796e44863dd22fa635b042df1bf16673"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "abf8eb360e79a6c2a837751fbd4d3d24"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "6018e1dfeaca7fe83f8d0b00eb0dd049"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // from Arne.F
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "f0b276781f47c130f423ec9679fe9ed9"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // from VooD
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "8909b41596913b3f5deaf3c9f1017b01"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // floppy 1.8 from clemmy
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "747861d2a9c643c59fdab570df5b9093"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // from gourry
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "ef08c8c237ee1473fd52578303fc36df"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
+ { "GEMCUT.EMC", 0, "2a3f44e179f1e9f7643e90083c747571", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH
+ },
+ KYRA1_AMIGA_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
+ { "GEMCUT.EMC", 0, "74f99e9ed99abf8d0429826d78485a2a", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH
+ },
+ KYRA1_AMIGA_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "GEMCUT.EMC", 0, "796e44863dd22fa635b042df1bf16673", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIGM
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+
+ { // FM-TOWNS version
+ {
+ "kyra1",
+ 0,
+ {
+ { "EMC.PAK", 0, "a046bb0b422061aab8e4c4689400343a", -1 },
+ { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA1_TOWNS_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
+ { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::JA_JPN,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA1_TOWNS_SJIS_FLAGS
+ },
+
+ // PC-9801 floppy + CD / PC-9821 floppy version are all using the same data files,
+ // thus we will mark it as non CD game.
+ {
+ {
+ "kyra1",
+ "",
+ {
+ { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
+ { "MUSIC98.PAK", 0, "02fc212f799331b769b274e33d87b37f", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ KYRA1_TOWNS_SJIS_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "fac399fe62f98671e56a005c5e94e39f"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "230f54e6afc007ab4117159181a1c722"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "b037c41768b652a040360ffa3556fd2a"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+
+ { // italian fan translation see fr#1727941 "KYRA: add Italian CD Version to kyra.dat"
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "d8327fc4b7a72b23c900fa13aef4093a"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+
+ { // Kyra 1 Mac CD as mentioned in fr #2766454 "KYRA1: Add support for Macintosh CD" by nnooiissee
+ {
+ "kyra1",
+ "CD",
+ {
+ { "GEMCUT.PAK", 0, "d3d4b281cd357230aabcec46843d04bd", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_CD,
+ Common::GUIO_NONE
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ {
+ { "GEMCUT.PAK", 0, "4a0cb720e824295bcbccbd1407652110", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformMacintosh,
+ ADGF_CD,
+ Common::GUIO_NONE
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ {
+ { "GEMCUT.PAK", 0, "b71ee090aa12e80ed2ba068826d92bed", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformMacintosh,
+ ADGF_CD,
+ Common::GUIO_NONE
+ },
+ KYRA1_CD_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ "Demo",
+ AD_ENTRY1("DEMO1.WSA", "fb722947d94897512b13b50cc84fd648"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_DEMO_FLAGS
+ },
+
+ { // Special Kyrandia 1 CD demo
+ {
+ "kyra1",
+ "Demo/CD",
+ AD_ENTRY1("INTRO.VRM", "e3045fb69b8c29db84b8fda3ccbdac54"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_DEMO_CD_FLAGS
+ },
+
+ { // Floppy version
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WESTWOOD.001", "3f52dda68c4f7696c8309038be9f4151"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_CMP_FLAGS
+ },
+
+ { // Floppy version
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WESTWOOD.001", "d787b9559afddfe058b84c0b3a787224"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_CMP_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "1ba18be685ad8e5a0ab5d46a0ce4d345"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "262fb69dd8e52e596c7aefc6456f7c1b"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "f7de11506b4c8fdf64bc763206c3e4e7"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "e0a70c31b022cb4bb3061890020fc27c"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // CD version
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FLAGS
+ },
+
+ // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
+ { // CD version
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ { // Interactive Demo
+ {
+ "kyra2",
+ "CD/Demo",
+ AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_DEMO_FLAGS
+ },
+
+ { // Interactive Demo
+ {
+ "kyra2",
+ "CD/Demo",
+ AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_DEMO_FLAGS
+ },
+
+ { // Interactive Demo
+ {
+ "kyra2",
+ "CD/Demo",
+ AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_DEMO_FLAGS
+ },
+
+ { // Non-Interactive Demos
+ {
+ "kyra2",
+ "Demo",
+ AD_ENTRY1("VOC.PAK", "ecb3561b63749158172bf21528cf5f45"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_DEMO_FLAGS
+ },
+
+ { // FM-TOWNS
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::EN_ANY,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA2_TOWNS_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::JA_JPN,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA2_TOWNS_SJIS_FLAGS
+ },
+ { // PC-9821
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::EN_ANY,
+ Common::kPlatformPC98,
+ ADGF_CD,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ KYRA2_TOWNS_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_CD,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ KYRA2_TOWNS_SJIS_FLAGS
+ },
+
+ // Kyra3
+
+ // non installed version
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "WESTWOOD.001", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "WESTWOOD.001", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "WESTWOOD.001", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FLAGS
+ },
+
+ // installed version
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+
+ // Mac version
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformMacintosh,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformMacintosh,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+
+ // Spanish fan translation, see fr#1994040 "KYRA3: Add support for Spanish fan translation"
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
+ },
+
+ // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
+ },
+
+#ifdef ENABLE_LOL
+ // Lands of Lore CD
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "WESTWOOD.1", 0, "c656aa9a2b4032d341e3dc8e3525b917", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_CMP_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "WESTWOOD.1", 0, "3c61cb7de5b2ec452f5851f5075207ee", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_CMP_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
+ { "GENERAL.PAK", 0, "2aaa30e120c08af87196820e9dd4bf73", -1 },
+ { "CHAPTER7.PAK", 0, "eb92bf7ebb4e890add1233a6b0c810ff", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
+ { "GENERAL.PAK", 0, "0f1fabc1f67b772a30d8e05ece720ac5", -1 },
+ { "CHAPTER7.PAK", 0, "482308aba1c40ee32449b91b0c63b990", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
+ { "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 },
+ { "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 },
+ { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ LOL_PC98_SJIS_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Demo",
+ {
+ { "INTRO.PAK", 0, "4bc22a3b57f19a49212c5de58ab014d6", -1 },
+ { "INTROVOC.PAK", 0, "7e578e4f1da31c1f294e14a8e8f3cc44", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_DEMO_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Demo",
+ {
+ { "GENERAL.PAK", 0, "e94863d86c4597a2d581d05481c152ba", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_KYRA2_DEMO_FLAGS
+ },
+#endif // ENABLE_LOL
+
+ { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0, 0) }
+};
+
+const PlainGameDescriptor gameList[] = {
+ { "kyra1", "The Legend of Kyrandia" },
+ { "kyra2", "The Legend of Kyrandia: The Hand of Fate" },
+ { "kyra3", "The Legend of Kyrandia: Malcolm's Revenge" },
+#ifdef ENABLE_LOL
+ { "lol", "Lands of Lore: The Throne of Chaos" },
+#endif // ENABLE_LOL
+ { 0, 0 }
+};
+
+} // End of anonymous namespace
diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp
index c8b7e8ec4f..9a1d750391 100644
--- a/engines/kyra/gui_lok.cpp
+++ b/engines/kyra/gui_lok.cpp
@@ -579,7 +579,7 @@ void GUI_LoK::setupSavegames(Menu &menu, int num) {
for (int i = startSlot; i < num; ++i)
menu.item[i].enabled = 0;
- KyraEngine_v1::SaveHeader header;
+ KyraEngine_LoK::SaveHeader header;
for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); i++) {
if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header))) {
Common::strlcpy(savenames[i], header.description.c_str(), ARRAYSIZE(savenames[0]));
diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp
index ded1326110..2c86073892 100644
--- a/engines/kyra/gui_lol.cpp
+++ b/engines/kyra/gui_lol.cpp
@@ -2611,7 +2611,7 @@ void GUI_LoL::updateSavegameList() {
if (_savegameListSize) {
Common::sort(_saveSlots.begin(), _saveSlots.end(), Common::Greater<int>());
- KyraEngine_v1::SaveHeader header;
+ LoLEngine::SaveHeader header;
Common::InSaveFile *in;
_savegameList = new char *[_savegameListSize];
@@ -2658,7 +2658,7 @@ int GUI_LoL::getInput() {
if (_currentMenu == &_savenameMenu) {
_vm->updateInput();
- for (Common::List<KyraEngine_v1::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) {
+ for (Common::List<LoLEngine::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) {
if (evt->event.type == Common::EVENT_KEYDOWN)
_keyPressed = evt->event.kbd;
}
diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp
index de19228d16..fe4b54d09b 100644
--- a/engines/kyra/gui_v2.cpp
+++ b/engines/kyra/gui_v2.cpp
@@ -452,7 +452,7 @@ void GUI_v2::setupSavegameNames(Menu &menu, int num) {
if (_isSaveMenu && _savegameOffset == 0)
startSlot = 1;
- KyraEngine_v1::SaveHeader header;
+ KyraEngine_v2::SaveHeader header;
Common::InSaveFile *in;
for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); ++i) {
if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header)) != 0) {
diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp
index 2716f0b285..0fafaa15ce 100644
--- a/engines/kyra/kyra_hof.cpp
+++ b/engines/kyra/kyra_hof.cpp
@@ -679,7 +679,7 @@ void KyraEngine_HoF::updateWithText() {
restorePage3();
drawAnimObjects();
- if (textEnabled() && _chatText) {
+ if (_chatTextEnabled && _chatText) {
int pageBackUp = _screen->_curPage;
_screen->_curPage = 2;
objectChatPrintText(_chatText, _chatObject);
@@ -1996,9 +1996,10 @@ void KyraEngine_HoF::writeSettings() {
}
void KyraEngine_HoF::readSettings() {
+ KyraEngine_v2::readSettings();
+
int talkspeed = ConfMan.getInt("talkspeed");
_configTextspeed = (talkspeed*95)/255 + 2;
- KyraEngine_v1::readSettings();
}
} // End of namespace Kyra
diff --git a/engines/kyra/kyra_lok.cpp b/engines/kyra/kyra_lok.cpp
index cf61b58326..159230e928 100644
--- a/engines/kyra/kyra_lok.cpp
+++ b/engines/kyra/kyra_lok.cpp
@@ -234,6 +234,7 @@ Common::Error KyraEngine_LoK::init() {
_talkingCharNum = -1;
_charSayUnk3 = -1;
+ _disabledTalkAnimObject = _enabledTalkAnimObject = 0;
memset(_currSentenceColor, 0, 3);
_startSentencePalIndex = -1;
_fadeText = false;
diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h
index c0c9bf06c4..50f36d7b71 100644
--- a/engines/kyra/kyra_lok.h
+++ b/engines/kyra/kyra_lok.h
@@ -132,7 +132,7 @@ public:
int _paletteChanged;
int16 _northExitHeight;
- typedef void (KyraEngine_LoK::*IntroProc)();
+ typedef bool (KyraEngine_LoK::*IntroProc)();
// static data access
const char * const *seqWSATable() { return _seq_WSATable; }
@@ -157,11 +157,12 @@ protected:
// -> intro
void seq_intro();
- void seq_introLogos();
- void seq_introStory();
- void seq_introMalcolmTree();
- void seq_introKallakWriting();
- void seq_introKallakMalcolm();
+ bool seq_introPublisherLogos();
+ bool seq_introLogos();
+ bool seq_introStory();
+ bool seq_introMalcolmTree();
+ bool seq_introKallakWriting();
+ bool seq_introKallakMalcolm();
// -> ingame animations
void seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly);
@@ -318,7 +319,7 @@ protected:
// chat
// -> process
void characterSays(int vocFile, const char *chatStr, int8 charNum, int8 chatDuration);
- void waitForChatToFinish(int vocFile, int16 chatDuration, const char *str, uint8 charNum);
+ void waitForChatToFinish(int vocFile, int16 chatDuration, const char *str, uint8 charNum, const bool printText);
// -> initialization
int initCharacterChat(int8 charNum);
@@ -373,20 +374,24 @@ protected:
//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 timerTulipCreator(int timerNum);
+ void timerRubyCreator(int timerNum);
+ void timerAsInvisibleTimeout(int timerNum);
+ void timerAsWillowispTimeout(int timerNum);
void checkAmuletAnimFlags();
void timerRedrawAmulet(int timerNum);
+ void timerLavenderRoseCreator(int timerNum);
+ void timerAcornCreator(int timerNum);
+ void timerBlueberryCreator(int timerNum);
void timerFadeText(int timerNum);
- void updateAnimFlag1(int timerNum);
- void updateAnimFlag2(int timerNum);
+ void timerWillowispFrameTimer(int timerNum);
+ void timerInvisibleFrameTimer(int timerNum);
void drawAmulet();
void setTextFadeTimerCountdown(int16 countdown);
void setWalkspeed(uint8 newSpeed);
+ void setItemCreationFlags(int offset, int count);
+
int buttonInventoryCallback(Button *caller);
int buttonAmuletCallback(Button *caller);
@@ -472,6 +477,8 @@ protected:
int8 _charSayUnk2;
int8 _charSayUnk3;
int8 _currHeadShape;
+ int8 _disabledTalkAnimObject;
+ int8 _enabledTalkAnimObject;
uint8 _currSentenceColor[3];
int8 _startSentencePalIndex;
bool _fadeText;
diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp
index cf90c73dbd..2169e5283f 100644
--- a/engines/kyra/kyra_mr.cpp
+++ b/engines/kyra/kyra_mr.cpp
@@ -369,9 +369,7 @@ void KyraEngine_MR::playVQA(const char *name) {
VQAMovie vqa(this, _system);
char filename[20];
- int size = 0; // TODO: Movie size is 0, 1 or 2.
-
- snprintf(filename, sizeof(filename), "%s%d.VQA", name, size);
+ snprintf(filename, sizeof(filename), "%s%d.VQA", name, _configVQAQuality);
if (vqa.open(filename)) {
for (int i = 0; i < 4; ++i) {
@@ -1129,7 +1127,7 @@ void KyraEngine_MR::updateWithText() {
restorePage3();
drawAnimObjects();
- if (textEnabled() && _chatText) {
+ if (_chatTextEnabled && _chatText) {
int curPage = _screen->_curPage;
_screen->_curPage = 2;
objectChatPrintText(_chatText, _chatObject);
@@ -1460,6 +1458,8 @@ void KyraEngine_MR::registerDefaultSettings() {
ConfMan.registerDefault("studio_audience", true);
ConfMan.registerDefault("skip_support", true);
ConfMan.registerDefault("helium_mode", false);
+ // 0 - best, 1 - mid, 2 - low
+ ConfMan.registerDefault("video_quality", 0);
}
void KyraEngine_MR::writeSettings() {
@@ -1490,11 +1490,12 @@ void KyraEngine_MR::writeSettings() {
}
void KyraEngine_MR::readSettings() {
- KyraEngine_v1::readSettings();
+ KyraEngine_v2::readSettings();
_configStudio = ConfMan.getBool("studio_audience");
_configSkip = ConfMan.getBool("skip_support");
_configHelium = ConfMan.getBool("helium_mode");
+ _configVQAQuality = CLIP(ConfMan.getInt("video_quality"), 0, 2);
}
} // End of namespace Kyra
diff --git a/engines/kyra/kyra_mr.h b/engines/kyra/kyra_mr.h
index 773b0a1699..36b937f2a8 100644
--- a/engines/kyra/kyra_mr.h
+++ b/engines/kyra/kyra_mr.h
@@ -73,6 +73,7 @@ private:
bool _configStudio;
bool _configSkip;
bool _configHelium;
+ int _configVQAQuality;
void registerDefaultSettings();
void writeSettings();
diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp
index 00b32425c2..1c27716a67 100644
--- a/engines/kyra/kyra_v1.cpp
+++ b/engines/kyra/kyra_v1.cpp
@@ -100,13 +100,14 @@ void KyraEngine_v1::pauseEngineIntern(bool pause) {
Common::Error KyraEngine_v1::init() {
// Setup mixer
- _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"));
+ syncSoundSettings();
if (!_flags.useDigSound) {
- // We prefer AdLib over MIDI, since generally AdLib is better supported
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_MIDI | MDT_ADLIB);
+ // We prefer AdLib over MIDI in Kyra 1, since it offers MT-32 support only, most users don't have a real
+ // MT-32/LAPC1/CM32L/CM64 device and AdLib sounds better than our incomplete MT-32 emulator and also better than
+ // MT-32/GM mapping. For Kyra 2 and LoL which have real GM tracks which sound better than AdLib tracks we prefer GM
+ // since most users have a GM compatible device.
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : 0));
if (_flags.platform == Common::kPlatformFMTowns) {
if (_flags.gameID == GI_KYRA1)
@@ -120,24 +121,24 @@ Common::Error KyraEngine_v1::init() {
_sound = new SoundTownsPC98_v2(this, _mixer);
} else if (_flags.platform == Common::kPlatformAmiga) {
_sound = new SoundAmiga(this, _mixer);
- } else if (midiDriver == MD_ADLIB) {
+ } else if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
_sound = new SoundAdLibPC(this, _mixer);
} else {
Sound::kType type;
- if (midiDriver == MD_PCSPK)
+ if (MidiDriver::getMusicType(dev) == MT_PCSPK)
type = Sound::kPCSpkr;
- else if (midiDriver == MD_MT32 || ConfMan.getBool("native_mt32"))
+ else if (MidiDriver::getMusicType(dev) == MT_MT32 || ConfMan.getBool("native_mt32"))
type = Sound::kMidiMT32;
else
type = Sound::kMidiGM;
MidiDriver *driver = 0;
- if (midiDriver == MD_PCSPK) {
+ if (MidiDriver::getMusicType(dev) == MT_PCSPK) {
driver = new MidiDriver_PCSpeaker(_mixer);
} else {
- driver = MidiDriver::createMidi(midiDriver);
+ driver = MidiDriver::createMidi(dev);
if (type == Sound::kMidiMT32)
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
}
@@ -256,7 +257,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag)
int keys = 0;
int8 mouseWheel = 0;
- while (_eventList.size()) {
+ while (!_eventList.empty()) {
Common::Event event = *_eventList.begin();
bool breakLoop = false;
@@ -280,6 +281,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag)
if (event.kbd.keycode == Common::KEYCODE_d) {
if (_debugger)
_debugger->attach();
+ breakLoop = true;
} else if (event.kbd.keycode == Common::KEYCODE_q) {
quitGame();
}
@@ -333,7 +335,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag)
break;
}
- if (_debugger && _debugger->isAttached())
+ if (_debugger)
_debugger->onFrame();
if (breakLoop)
@@ -623,6 +625,10 @@ uint8 KyraEngine_v1::getVolume(kVolumeEntry vol) {
void KyraEngine_v1::syncSoundSettings() {
Engine::syncSoundSettings();
+ // We need to use this here to allow the subtitle options to be changed
+ // through the GMM's options dialog.
+ readSettings();
+
if (_sound)
_sound->updateVolumeSettings();
}
diff --git a/engines/kyra/kyra_v2.cpp b/engines/kyra/kyra_v2.cpp
index 34284a8e20..53c57c21cd 100644
--- a/engines/kyra/kyra_v2.cpp
+++ b/engines/kyra/kyra_v2.cpp
@@ -63,6 +63,7 @@ KyraEngine_v2::KyraEngine_v2(OSystem *system, const GameFlags &flags, const Engi
_chatVocLow = -1;
_chatText = 0;
_chatObject = -1;
+ _chatTextEnabled = false;
memset(_hiddenItems, -1, sizeof(_hiddenItems));
diff --git a/engines/kyra/kyra_v2.h b/engines/kyra/kyra_v2.h
index 6aaa8c3687..6414040344 100644
--- a/engines/kyra/kyra_v2.h
+++ b/engines/kyra/kyra_v2.h
@@ -350,6 +350,7 @@ protected:
int _chatObject;
uint32 _chatEndTime;
int _chatVocHigh, _chatVocLow;
+ bool _chatTextEnabled;
EMCData _chatScriptData;
EMCState _chatScriptState;
diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp
index 5a066e5d0c..03d52ec4ac 100644
--- a/engines/kyra/lol.cpp
+++ b/engines/kyra/lol.cpp
@@ -810,8 +810,8 @@ void LoLEngine::startup() {
pal.fill(0, 1, 0x3F);
pal.fill(2, 126, 0x3F);
pal.fill(192, 4, 0x3F);
- _screen->generateOverlay(pal, _screen->_paletteOverlay1, 1, 96);
- _screen->generateOverlay(pal, _screen->_paletteOverlay2, 144, 65);
+ _screen->generateOverlay(pal, _screen->_paletteOverlay1, 1, 96, 254);
+ _screen->generateOverlay(pal, _screen->_paletteOverlay2, 144, 65, 254);
_screen->copyPalette(0, 1);
}
@@ -4291,7 +4291,7 @@ void LoLEngine::drawMapPage(int pageNum) {
if (!_defaultLegendData[ii].enable)
continue;
_screen->copyBlockAndApplyOverlay(_screen->_curPage, 235, (tY << 3) + 21 + yOffset, _screen->_curPage, 235 + xOffset, (tY << 3) + 21 + yOffset, 7, 6, 0, _mapOverlay);
- _screen->drawShape(_screen->_curPage, _automapShapes[_defaultLegendData[ii].shapeIndex << 2], 232 + xOffset, (tY << 3) + 18 + yOffset + _defaultLegendData[ii].x, 0, 0);
+ _screen->drawShape(_screen->_curPage, _automapShapes[_defaultLegendData[ii].shapeIndex << 2], 232 + xOffset, (tY << 3) + 18 + yOffset + _defaultLegendData[ii].y, 0, 0);
printMapText(_defaultLegendData[ii].stringId, 244 + xOffset, (tY << 3) + 22 + yOffset);
tY++;
}
diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h
index b5a657ac15..57c127a94f 100644
--- a/engines/kyra/lol.h
+++ b/engines/kyra/lol.h
@@ -256,7 +256,7 @@ struct LevelTempData {
struct MapLegendData {
uint8 shapeIndex;
bool enable;
- int8 x;
+ int8 y;
uint16 stringId;
};
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index b9006431d7..4708041cf7 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -84,6 +84,7 @@ MODULE_OBJS += \
sequences_lol.o \
sound_lol.o \
sprites_lol.o \
+ staticres_lol.o \
text_lol.o \
timer_lol.o
endif
diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h
index 7983be9a68..d572c1ac54 100644
--- a/engines/kyra/resource.h
+++ b/engines/kyra/resource.h
@@ -209,6 +209,7 @@ enum KyraResources {
k1CreditsStrings,
+ k1TownsMusicFadeTable,
k1TownsSFXwdTable,
k1TownsSFXbtTable,
k1TownsCDATable,
diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp
index 959d89f0ad..56e1c73d0a 100644
--- a/engines/kyra/saveload.cpp
+++ b/engines/kyra/saveload.cpp
@@ -122,7 +122,7 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::Seekab
header.thumbnail = 0;
}
} else {
- Graphics::skipThumbnailHeader(*in);
+ Graphics::skipThumbnail(*in);
}
}
diff --git a/engines/kyra/scene_lol.cpp b/engines/kyra/scene_lol.cpp
index ddc6e41bec..bf3320486a 100644
--- a/engines/kyra/scene_lol.cpp
+++ b/engines/kyra/scene_lol.cpp
@@ -468,7 +468,7 @@ void LoLEngine::loadLevelGraphics(const char *file, int specialColor, int weight
for (int i = 0; i < 7; i++) {
weight = 100 - (i * _lastSpecialColorWeight);
weight = (weight > 0) ? (weight * 255) / 100 : 0;
- _screen->generateLevelOverlay(tpal, _screen->getLevelOverlay(i), _lastSpecialColor, weight);
+ _screen->generateOverlay(tpal, _screen->getLevelOverlay(i), _lastSpecialColor, weight);
int l = _flags.use16ColorMode ? 256 : 128;
uint8 *levelOverlay = _screen->getLevelOverlay(i);
diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp
index 875200895a..bd0a1fe544 100644
--- a/engines/kyra/scene_mr.cpp
+++ b/engines/kyra/scene_mr.cpp
@@ -79,11 +79,9 @@ void KyraEngine_MR::enterNewScene(uint16 sceneId, int facing, int unk1, int unk2
musicUpdate(0);
uint32 waitUntilTimer = 0;
- bool newSoundFile = false;
if (_lastMusicCommand != _sceneList[sceneId].sound) {
fadeOutMusic(60);
waitUntilTimer = _system->getMillis() + 60 * _tickLength;
- newSoundFile = true;
}
_chatAltFlag = false;
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
index 7f3959d5fe..ade9886c2e 100644
--- a/engines/kyra/screen.cpp
+++ b/engines/kyra/screen.cpp
@@ -611,14 +611,6 @@ void Screen::fadePalette(const Palette &pal, int delay, const UpdateFunctor *upF
_vm->delay((delayAcc >> 8) * 1000 / 60);
delayAcc &= 0xFF;
}
-
- if (_vm->shouldQuit()) {
- setScreenPalette(pal);
- if (upFunc && upFunc->isValid())
- (*upFunc)();
- else
- _system->updateScreen();
- }
}
void Screen::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
diff --git a/engines/kyra/screen_lol.cpp b/engines/kyra/screen_lol.cpp
index e350d2c977..be3dbe5b21 100644
--- a/engines/kyra/screen_lol.cpp
+++ b/engines/kyra/screen_lol.cpp
@@ -183,68 +183,6 @@ void Screen_LoL::generateGrayOverlay(const Palette &srcPal, uint8 *grayOverlay,
grayOverlay[i] = findLeastDifferentColor(tmpPal.getData() + 3 * i, srcPal, 0, lastColor, skipSpecialColors);
}
-uint8 *Screen_LoL::generateLevelOverlay(const Palette &srcPal, uint8 *ovl, int opColor, int weight) {
- if (!ovl)
- return ovl;
-
- if (weight > 255)
- weight = 255;
-
- const uint8 *srt = srcPal.getData();
-
- uint16 r = srt[opColor * 3];
- uint16 g = srt[opColor * 3 + 1];
- uint16 b = srt[opColor * 3 + 2];
-
- uint8 *d = ovl;
- *d++ = 0;
-
- for (int i = 1; i != 256; i++) {
- uint16 a = srt[i * 3];
- uint8 dr = a - ((((a - r) * (weight >> 1)) << 1) >> 8);
- a = srt[i * 3 + 1];
- uint8 dg = a - ((((a - g) * (weight >> 1)) << 1) >> 8);
- a = srt[i * 3 + 2];
- uint8 db = a - ((((a - b) * (weight >> 1)) << 1) >> 8);
-
- int l = opColor;
- int m = _use16ColorMode ? 0xffff : 0x7fff;
- int ii = _use16ColorMode ? 255 : 127;
- int x = 1;
- const uint8 *s = srt + 3;
-
- do {
- if (!_use16ColorMode && i == x) {
- s += 3;
- } else {
- int t = *s++ - dr;
- int c = t * t;
- t = *s++ - dg;
- c += (t * t);
- t = *s++ - db;
- c += (t * t);
-
- if (!c) {
- l = x;
- break;
- }
-
- if (c <= m) {
- if (!_use16ColorMode || (x == opColor || i != x)) {
- m = c;
- l = x;
- }
- }
- }
- x++;
- } while (--ii);
-
- *d++ = l & 0xff;
- }
-
- return ovl;
-}
-
void Screen_LoL::createTransparencyTablesIntern(const uint8 *ovl, int a, const uint8 *fxPal1, const uint8 *fxPal2, uint8 *outTable1, uint8 *outTable2, int b) {
Palette screenPal(256);
screenPal.copy(fxPal2, 0, 256);
diff --git a/engines/kyra/screen_lol.h b/engines/kyra/screen_lol.h
index cdd18f98f6..52e66df1ec 100644
--- a/engines/kyra/screen_lol.h
+++ b/engines/kyra/screen_lol.h
@@ -79,7 +79,6 @@ public:
Palette **generateFadeTable(Palette **dst, Palette *src1, Palette *src2, int numTabs);
void generateGrayOverlay(const Palette &Pal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool skipSpecialColors);
- uint8 *generateLevelOverlay(const Palette &Pal, uint8 *ovl, int opColor, int weight);
uint8 *getLevelOverlay(int index) { return _levelOverlays[index]; }
void createTransparencyTablesIntern(const uint8 *ovl, int a, const uint8 *fxPal1, const uint8 *fxPal2, uint8 *outTable1, uint8 *outTable2, int b);
diff --git a/engines/kyra/screen_v2.cpp b/engines/kyra/screen_v2.cpp
index 919b9086f3..3907f844cb 100644
--- a/engines/kyra/screen_v2.cpp
+++ b/engines/kyra/screen_v2.cpp
@@ -38,38 +38,67 @@ Screen_v2::~Screen_v2() {
delete[] _wsaFrameAnimBuffer;
}
-uint8 *Screen_v2::generateOverlay(const Palette &pal, uint8 *buffer, int startColor, uint16 factor) {
+uint8 *Screen_v2::generateOverlay(const Palette &pal, uint8 *buffer, int opColor, uint weight, int maxColor) {
if (!buffer)
return buffer;
- factor = MIN<uint16>(255, factor);
- factor >>= 1;
- factor &= 0xFF;
+ weight = MIN<uint>(weight, 255) >> 1;
- const byte col1 = pal[startColor * 3 + 0];
- const byte col2 = pal[startColor * 3 + 1];
- const byte col3 = pal[startColor * 3 + 2];
+ const byte opR = pal[opColor * 3 + 0];
+ const byte opG = pal[opColor * 3 + 1];
+ const byte opB = pal[opColor * 3 + 2];
uint8 *dst = buffer;
*dst++ = 0;
- for (int i = 1; i != 255; ++i) {
- uint8 processedPalette[3];
- byte col;
+ int maxIndex = maxColor;
+ if (maxIndex == -1) {
+ if (_vm->gameFlags().gameID == GI_LOL) {
+ if (_use16ColorMode)
+ maxIndex = 255;
+ else
+ maxIndex = 127;
+ } else {
+ maxIndex = 255;
+ }
+ }
+
+ for (int i = 1; i != 256; ++i) {
+ const byte curR = pal[i * 3 + 0] - ((((pal[i * 3 + 0] - opR) * weight) >> 7) & 0x7F);
+ const byte curG = pal[i * 3 + 1] - ((((pal[i * 3 + 1] - opG) * weight) >> 7) & 0x7F);
+ const byte curB = pal[i * 3 + 2] - ((((pal[i * 3 + 2] - opB) * weight) >> 7) & 0x7F);
+
+ uint16 idxSum = _use16ColorMode ? 0xFFFF : 0x7FFF;
+ byte index = opColor;
+
+ for (int curIdx = 1; curIdx <= maxIndex; ++curIdx) {
+ if (!_use16ColorMode && i == curIdx)
+ continue;
- col = pal[i * 3 + 0];
- col -= ((((col - col1) * factor) << 1) >> 8) & 0xFF;
- processedPalette[0] = col;
+ int diff = 0;
+ uint16 sum = 0;
- col = pal[i * 3 + 1];
- col -= ((((col - col2) * factor) << 1) >> 8) & 0xFF;
- processedPalette[1] = col;
+ diff = pal[curIdx * 3 + 0] - curR;
+ sum += diff * diff;
+ diff = pal[curIdx * 3 + 1] - curG;
+ sum += diff * diff;
+ diff = pal[curIdx * 3 + 2] - curB;
+ sum += diff * diff;
- col = pal[i * 3 + 2];
- col -= ((((col - col3) * factor) << 1) >> 8) & 0xFF;
- processedPalette[2] = col;
+ if (!sum) {
+ index = curIdx;
+ break;
+ }
+
+ if (sum <= idxSum) {
+ if (!_use16ColorMode || (curIdx == opColor || curIdx != i)) {
+ idxSum = sum;
+ index = curIdx;
+ }
+ }
+ }
- *dst++ = findLeastDifferentColor(processedPalette, pal, 1, 255) + 1;
+ *dst++ = index;
}
return buffer;
diff --git a/engines/kyra/screen_v2.h b/engines/kyra/screen_v2.h
index 7be68e7b6d..92aeb3525d 100644
--- a/engines/kyra/screen_v2.h
+++ b/engines/kyra/screen_v2.h
@@ -40,7 +40,7 @@ public:
void checkedPageUpdate(int srcPage, int dstPage);
// palette handling
- uint8 *generateOverlay(const Palette &pal, uint8 *buffer, int color, uint16 factor);
+ uint8 *generateOverlay(const Palette &pal, uint8 *buffer, int color, uint weight, int maxColor = -1);
void applyOverlay(int x, int y, int w, int h, int pageNum, const uint8 *overlay);
int findLeastDifferentColor(const uint8 *paletteEntry, const Palette &pal, uint8 firstColor, uint16 numColors, bool skipSpecialColors = false);
diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp
index 4ec6e7f349..20bc8abec5 100644
--- a/engines/kyra/script_tim.cpp
+++ b/engines/kyra/script_tim.cpp
@@ -159,7 +159,7 @@ TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpc
_tim->opcodes = opcodes;
IFFParser iff(*stream);
- Common::Functor1Mem< Common::IFFChunk &, bool, TIMInterpreter > c(this, &TIMInterpreter::callback);
+ Common::Functor1Mem<Common::IFFChunk &, bool, TIMInterpreter> c(this, &TIMInterpreter::callback);
iff.parse(c);
if (!_tim->avtl)
@@ -170,7 +170,7 @@ TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpc
delete stream;
- int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs;
+ const int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs;
for (int i = 0; i < num; ++i)
_tim->func[i].avtl = _tim->avtl + _tim->avtl[i];
diff --git a/engines/kyra/sequences_lok.cpp b/engines/kyra/sequences_lok.cpp
index 78e8ded9f7..c4bdc29f57 100644
--- a/engines/kyra/sequences_lok.cpp
+++ b/engines/kyra/sequences_lok.cpp
@@ -93,6 +93,7 @@ void KyraEngine_LoK::seq_intro() {
_res->loadPakFile("INTRO.VRM");
static const IntroProc introProcTable[] = {
+ &KyraEngine_LoK::seq_introPublisherLogos,
&KyraEngine_LoK::seq_introLogos,
&KyraEngine_LoK::seq_introStory,
&KyraEngine_LoK::seq_introMalcolmTree,
@@ -114,8 +115,13 @@ void KyraEngine_LoK::seq_intro() {
snd_playTheme(0, 2);
_text->setTalkCoords(144);
- for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i)
- (this->*introProcTable[i])();
+ for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i) {
+ if ((this->*introProcTable[i])() && !shouldQuit()) {
+ resetSkipFlag();
+ _screen->fadeToBlack();
+ _screen->clearPage(0);
+ }
+ }
_text->setTalkCoords(136);
delay(30 * _tickLength);
@@ -127,18 +133,32 @@ void KyraEngine_LoK::seq_intro() {
_res->unloadPakFile("INTRO.VRM");
}
-void KyraEngine_LoK::seq_introLogos() {
+bool KyraEngine_LoK::seq_introPublisherLogos() {
if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
_screen->loadBitmap("LOGO.CPS", 3, 3, &_screen->getPalette(0));
_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0);
_screen->updateScreen();
_screen->fadeFromBlack();
delay(90 * _tickLength);
- _screen->fadeToBlack();
- if (!_abortIntroFlag)
+ if (!_abortIntroFlag) {
+ _screen->fadeToBlack();
snd_playWanderScoreViaMap(_flags.platform == Common::kPlatformFMTowns ? 57 : 2, 0);
+ }
+ } else if (_flags.platform == Common::kPlatformMacintosh && _res->exists("MP_GOLD.CPS")) {
+ _screen->loadPalette("MP_GOLD.COL", _screen->getPalette(0));
+ _screen->loadBitmap("MP_GOLD.CPS", 3, 3, 0);
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0);
+ _screen->updateScreen();
+ _screen->fadeFromBlack();
+ delay(120 * _tickLength);
+ if (!_abortIntroFlag)
+ _screen->fadeToBlack();
}
+ return _abortIntroFlag;
+}
+
+bool KyraEngine_LoK::seq_introLogos() {
_screen->clearPage(0);
if (_flags.platform == Common::kPlatformAmiga) {
@@ -159,11 +179,8 @@ void KyraEngine_LoK::seq_introLogos() {
_screen->updateScreen();
_screen->fadeFromBlack();
- if (_seq->playSequence(_seq_WestwoodLogo, skipFlag()) || shouldQuit()) {
- _screen->fadeToBlack();
- _screen->clearPage(0);
- return;
- }
+ if (_seq->playSequence(_seq_WestwoodLogo, skipFlag()) || shouldQuit())
+ return true;
delay(60 * _tickLength);
@@ -174,16 +191,14 @@ void KyraEngine_LoK::seq_introLogos() {
Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
- if ((_seq->playSequence(_seq_KyrandiaLogo, skipFlag()) && !seq_skipSequence()) || shouldQuit()) {
- _screen->fadeToBlack();
- _screen->clearPage(0);
- return;
- }
+ if (_seq->playSequence(_seq_KyrandiaLogo, skipFlag()) || shouldQuit())
+ return true;
+
_screen->setFont(of);
_screen->fillRect(0, 179, 319, 199, 0);
if (shouldQuit())
- return;
+ return false;
if (_flags.platform == Common::kPlatformAmiga) {
_screen->copyPalette(0, 2);
@@ -225,20 +240,20 @@ void KyraEngine_LoK::seq_introLogos() {
} while (!doneFlag && !shouldQuit() && !_abortIntroFlag);
}
- if (shouldQuit())
- return;
+ if (_abortIntroFlag || shouldQuit())
+ return true;
- _seq->playSequence(_seq_Forest, true);
+ return _seq->playSequence(_seq_Forest, true);
}
-void KyraEngine_LoK::seq_introStory() {
+bool KyraEngine_LoK::seq_introStory() {
_screen->clearPage(3);
_screen->clearPage(0);
// HACK: The Italian fan translation uses an special text screen here
// so we show it even when text is disabled
if (!textEnabled() && speechEnabled() && _flags.lang != Common::IT_ITA)
- return;
+ return false;
if ((_flags.lang == Common::EN_ANY && !_flags.isTalkie && _flags.platform == Common::kPlatformPC) || _flags.platform == Common::kPlatformAmiga)
_screen->loadBitmap("TEXT.CPS", 3, 3, &_screen->getPalette(0));
@@ -292,25 +307,30 @@ void KyraEngine_LoK::seq_introStory() {
_screen->updateScreen();
delay(360 * _tickLength);
+
+ return _abortIntroFlag;
}
-void KyraEngine_LoK::seq_introMalcolmTree() {
+bool KyraEngine_LoK::seq_introMalcolmTree() {
_screen->_curPage = 0;
_screen->clearPage(3);
- _seq->playSequence(_seq_MalcolmTree, true);
+ return _seq->playSequence(_seq_MalcolmTree, true);
}
-void KyraEngine_LoK::seq_introKallakWriting() {
+bool KyraEngine_LoK::seq_introKallakWriting() {
_seq->makeHandShapes();
_screen->setAnimBlockPtr(5060);
_screen->_charWidth = -2;
_screen->clearPage(3);
- _seq->playSequence(_seq_KallakWriting, true);
+ const bool skipped = _seq->playSequence(_seq_KallakWriting, true);
+ _seq->freeHandShapes();
+
+ return skipped;
}
-void KyraEngine_LoK::seq_introKallakMalcolm() {
+bool KyraEngine_LoK::seq_introKallakMalcolm() {
_screen->clearPage(3);
- _seq->playSequence(_seq_KallakMalcolm, true);
+ return _seq->playSequence(_seq_KallakMalcolm, true);
}
void KyraEngine_LoK::seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly) {
diff --git a/engines/kyra/sound_intern.h b/engines/kyra/sound_intern.h
index f4aab4db29..f8738bc791 100644
--- a/engines/kyra/sound_intern.h
+++ b/engines/kyra/sound_intern.h
@@ -31,7 +31,9 @@
#include "common/mutex.h"
-#include "sound/softsynth/ym2612.h"
+#include "sound/softsynth/fmtowns_pc98/towns_pc98_driver.h"
+#include "sound/softsynth/fmtowns_pc98/towns_euphony.h"
+
#include "sound/softsynth/emumidi.h"
#include "sound/midiparser.h"
@@ -99,10 +101,7 @@ private:
Common::Mutex _mutex;
};
-class Towns_EuphonyDriver;
-class TownsPC98_OpnDriver;
-
-class SoundTowns : public MidiDriver, public Sound {
+class SoundTowns : public Sound {
public:
SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundTowns();
@@ -119,43 +118,35 @@ public:
void haltTrack();
void playSoundEffect(uint8);
+ void stopAllSoundEffects();
void beginFadeOut();
- //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();
-
- //Channel allocation functions
- MidiChannel *allocateChannel() { return 0; }
- MidiChannel *getPercussionChannel() { return 0; }
-
- static float calculatePhaseStep(int8 semiTone, int8 semiToneRootkey,
- uint32 sampleRate, uint32 outputRate, int32 pitchWheel);
+ void updateVolumeSettings();
private:
bool loadInstruments();
void playEuphonyTrack(uint32 offset, int loop);
- static void onTimer(void *data);
+ void fadeOutSoundEffects();
int _lastTrack;
Audio::AudioStream *_currentSFX;
Audio::SoundHandle _sfxHandle;
+ uint8 *_musicTrackData;
+
uint _sfxFileIndex;
uint8 *_sfxFileData;
+ uint8 _sfxChannel;
- Towns_EuphonyDriver * _driver;
- MidiParser * _parser;
-
+ TownsEuphonyDriver *_driver;
+
Common::Mutex _mutex;
+ bool _cdaPlaying;
+
+ const uint8 *_musicFadeTable;
const uint8 *_sfxBTTable;
const uint8 *_sfxWDTable;
};
@@ -180,11 +171,13 @@ public:
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx) { return -1; }
void playSoundEffect(uint8);
+ void updateVolumeSettings();
+
protected:
int _lastTrack;
uint8 *_musicTrackData;
uint8 *_sfxTrackData;
- TownsPC98_OpnDriver *_driver;
+ TownsPC98_AudioDriver *_driver;
};
class SoundTownsPC98_v2 : public Sound {
@@ -207,6 +200,8 @@ public:
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx);
void playSoundEffect(uint8 track);
+ void updateVolumeSettings();
+
protected:
Audio::AudioStream *_currentSFX;
int _lastTrack;
@@ -214,7 +209,7 @@ protected:
uint8 *_musicTrackData;
uint8 *_sfxTrackData;
- TownsPC98_OpnDriver *_driver;
+ TownsPC98_AudioDriver *_driver;
};
// PC Speaker MIDI driver
diff --git a/engines/kyra/sound_lok.cpp b/engines/kyra/sound_lok.cpp
index 1d0b334a09..40daa0b5bd 100644
--- a/engines/kyra/sound_lok.cpp
+++ b/engines/kyra/sound_lok.cpp
@@ -49,16 +49,14 @@ void KyraEngine_LoK::snd_playWanderScoreViaMap(int command, int restart) {
_lastMusicCommand = -1;
if (_flags.platform == Common::kPlatformFMTowns) {
- if (command == 1) {
- _sound->beginFadeOut();
- } else if (command >= 35 && command <= 38) {
+ if (command >= 35 && command <= 38) {
snd_playSoundEffect(command - 20);
} else if (command >= 2) {
if (_lastMusicCommand != command)
// the original does -2 here we handle this inside _sound->playTrack()
_sound->playTrack(command);
} else {
- _sound->haltTrack();
+ _sound->beginFadeOut();
}
_lastMusicCommand = command;
} else if (_flags.platform == Common::kPlatformPC98) {
diff --git a/engines/kyra/sound_lol.cpp b/engines/kyra/sound_lol.cpp
index 1bcb77c89d..c233987120 100644
--- a/engines/kyra/sound_lol.cpp
+++ b/engines/kyra/sound_lol.cpp
@@ -225,7 +225,7 @@ void LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) {
for (int i = 3; i > 0; i--) {
int dir = calcMonsterDirection(cbl & 0x1f, cbl >> 5, block & 0x1f, block >> 5);
- cbl += blockShiftTable[dir];
+ cbl = (cbl + blockShiftTable[dir]) & 0x3ff;
if (cbl != block) {
if (testWallFlag(cbl, 0, 1))
_environmentSfxVol >>= 1;
diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp
index 7eb151a64d..026c72de26 100644
--- a/engines/kyra/sound_midi.cpp
+++ b/engines/kyra/sound_midi.cpp
@@ -573,8 +573,12 @@ void SoundMidiPC::updateVolumeSettings() {
if (!_output)
return;
- int newMusVol = ConfMan.getInt("music_volume");
- _sfxVolume = ConfMan.getInt("sfx_volume");
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ const int newMusVol = (mute ? 0 : ConfMan.getInt("music_volume"));
+ _sfxVolume = (mute ? 0 : ConfMan.getInt("sfx_volume"));
_output->setSourceVolume(0, newMusVol, newMusVol != _musicVolume);
_musicVolume = newMusVol;
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index b0d3f994f2..750c6edfc2 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -23,7 +23,9 @@
*
*/
+#include "common/config-manager.h"
#include "common/system.h"
+
#include "kyra/resource.h"
#include "kyra/sound_intern.h"
#include "kyra/screen.h"
@@ -34,3765 +36,41 @@
#include "common/util.h"
-#define EUPHONY_FADEOUT_TICKS 600
-
namespace Kyra {
-enum EnvelopeState { s_ready, s_attacking, s_decaying, s_sustaining, s_releasing };
-
-class Towns_EuphonyChannel : public MidiChannel {
-public:
- Towns_EuphonyChannel() {}
- virtual ~Towns_EuphonyChannel() {}
-
- virtual void nextTick(int32 *outbuf, int buflen) = 0;
- virtual void rate(uint16 r) = 0;
-
-protected:
- uint16 _rate;
-};
-
-class Towns_EuphonyFmChannel : public Towns_EuphonyChannel {
-public:
- Towns_EuphonyFmChannel();
- virtual ~Towns_EuphonyFmChannel();
-
- void nextTick(int32 *outbuf, int buflen);
- void rate(uint16 r);
-
- // MidiChannel interface
- MidiDriver *device() { return 0; }
- byte getNumber() { return 0; }
- void release() { }
- void send(uint32) { }
- void noteOff(byte note);
- void noteOn(byte note, byte onVelo);
- void programChange(byte) {}
- void pitchBend(int16 value);
- void controlChange(byte control, byte value);
- void pitchBendFactor(byte) { }
- void sysEx_customInstrument(uint32 unused, const byte *instr);
-
-protected:
- Voice2612 *_voice;
-};
-
-class Towns_EuphonyPcmChannel : public Towns_EuphonyChannel {
-public:
- void nextTick(int32 *outbuf, int buflen);
- void rate(uint16 r);
-
- Towns_EuphonyPcmChannel();
- virtual ~Towns_EuphonyPcmChannel();
-
- // MidiChannel interface
- MidiDriver *device() { return 0; }
- byte getNumber() { return 0; }
- void release() { }
- void send(uint32 b) { }
- void noteOff(byte note);
- void noteOn(byte note, byte onVelo);
- void programChange(byte program) {}
- void pitchBend(int16 value);
- void controlChange(byte control, byte value);
- void pitchBendFactor(byte value) { }
- void sysEx_customInstrument(uint32 type, const byte *instr);
-
-protected:
- void velocity(int velo);
- void panPosition(int8 pan);
- void evpNextTick();
-
- int _ctrl7_volume;
- int16 _velocity;
- int16 _note;
- int32 _frequencyOffs;
- float _phase;
- int8 _current;
-
- struct Voice {
- char name[9];
- uint16 split[8];
- uint32 id[8];
- struct Snd {
- char name[9];
- int32 id;
- int32 numSamples;
- int32 loopStart;
- int32 loopLength;
- int32 samplingRate;
- int32 keyOffset;
- int32 keyNote;
- const int8 *_samples;
- } *_snd[8];
- struct Env {
- EnvelopeState state;
- int32 currentLevel;
- int32 rate;
- int32 tickCount;
- int32 totalLevel;
- int32 attackRate;
- int32 decayRate;
- int32 sustainLevel;
- int32 sustainRate;
- int32 releaseLevel;
- int32 releaseRate;
- int32 rootKeyOffset;
- int32 size;
- } *_env[8];
- } *_voice;
-};
-
-class Towns_EuphonyTrackQueue {
-public:
- Towns_EuphonyTrackQueue(Towns_EuphonyDriver *driver, Towns_EuphonyTrackQueue *last);
- ~Towns_EuphonyTrackQueue() {}
-
- Towns_EuphonyTrackQueue *release();
- void initDriver();
- void loadDataToCurrentPosition(uint8 *trackdata, uint32 size, bool loop = 0);
- void loadDataToEndOfQueue(uint8 *trackdata, uint32 size, bool loop = 0);
- void setPlayBackStatus(bool playing);
- bool isPlaying() const {return _playing; }
- uint8 *trackData() {return _trackData; }
-
- bool _loop;
- Towns_EuphonyTrackQueue *_next;
-
-private:
- uint8 *_trackData;
- uint8 *_used;
- uint8 *_fchan;
- uint8 *_wchan;
- bool _playing;
- Towns_EuphonyDriver *_driver;
- Towns_EuphonyTrackQueue *_last;
-};
-
-class Towns_EuphonyParser : public MidiParser {
-public:
- Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue);
- bool loadMusic (byte *data, uint32 size);
- int32 calculateTempo(int16 val);
-
-protected:
- void parseNextEvent (EventInfo &info);
- void resetTracking();
- void setup();
-
- byte *_enable;
- byte *_mode;
- byte *_channel;
- byte *_adjVelo;
- int8 *_adjNote;
-
- uint8 _firstBaseTickStep;
- uint8 _nextBaseTickStep;
- uint32 _initialTempo;
- uint32 _baseTick;
-
- byte _tempo[3];
- Towns_EuphonyTrackQueue *_queue;
-};
-
-class Towns_EuphonyDriver : public MidiDriver_Emulated {
-public:
- Towns_EuphonyDriver(Audio::Mixer *mixer);
- virtual ~Towns_EuphonyDriver();
-
- int open();
- void close();
- void send(uint32 b);
- void send(byte channel, uint32 b);
- uint32 property(int prop, uint32 param) { return 0; }
-
- void setPitchBendRange(byte channel, uint range) { }
- void loadFmInstruments(const byte *instr);
- void loadWaveInstruments(const byte *instr);
-
- Towns_EuphonyTrackQueue *queue() { return _queue; }
-
- MidiChannel *allocateChannel() { return 0; }
- MidiChannel *getPercussionChannel() { return 0; }
-
- void assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber);
- void assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber);
- void removeChannel(uint8 midiChannelNumber);
-
- void setVolume(int val = -1) { if (val >= 0) _volume = val; }
- int getVolume(int val = -1) { return _volume; }
-
- // AudioStream API
- bool isStereo() const { return true; }
- int getRate() const { return _mixer->getOutputRate(); }
-
- void fading(bool status = true);
-
-protected:
- void nextTick(int16 *buf1, int buflen);
- void rate(uint16 r);
-
- void generateSamples(int16 *buf, int len);
-
- Towns_EuphonyFmChannel *_fChannel[6];
- Towns_EuphonyPcmChannel *_wChannel[8];
- Towns_EuphonyChannel *_channel[16];
- Towns_EuphonyTrackQueue *_queue;
-
- int _volume;
- bool _fading;
- int16 _fadestate;
-
- uint8 *_fmInstruments;
- uint8 *_waveInstruments;
- int8 * _waveSounds[10];
-};
-
-Towns_EuphonyFmChannel::Towns_EuphonyFmChannel() {
- _voice = new Voice2612;
-}
-
-Towns_EuphonyFmChannel::~Towns_EuphonyFmChannel() {
- delete _voice;
-}
-
-void Towns_EuphonyFmChannel::noteOn(byte note, byte onVelo) {
- _voice->noteOn(note, onVelo);
-}
-
-void Towns_EuphonyFmChannel::noteOff(byte note) {
- _voice->noteOff(note);
-}
-
-void Towns_EuphonyFmChannel::controlChange(byte control, byte value) {
- if (control == 121) {
- // Reset controller
- delete _voice;
- _voice = new Voice2612;
- } else if (control == 10) {
- // pan position
- } else {
- _voice->setControlParameter(control, value);
- }
-}
-
-void Towns_EuphonyFmChannel::sysEx_customInstrument(uint32, const byte *fmInst) {
- _voice->_rate = _rate;
- _voice->setInstrument(fmInst);
-}
-
-void Towns_EuphonyFmChannel::pitchBend(int16 value) {
- _voice->pitchBend(value);
-}
-
-void Towns_EuphonyFmChannel::nextTick(int32 *outbuf, int buflen) {
- _voice->nextTick((int *)outbuf, buflen);
-}
-
-void Towns_EuphonyFmChannel::rate(uint16 r) {
- _rate = r;
- _voice->_rate = r;
-}
-
-Towns_EuphonyPcmChannel::Towns_EuphonyPcmChannel() {
- _voice = new Voice;
- for (uint8 i = 0; i < 8; i++) {
- _voice->_env[i] = new Voice::Env;
- _voice->_snd[i] = 0;
- }
-
- _ctrl7_volume = 127;
- velocity(0);
- _frequencyOffs = 0x2000;
- _current = -1;
-}
-
-Towns_EuphonyPcmChannel::~Towns_EuphonyPcmChannel() {
- for (uint8 i = 0; i < 8; i++) {
- if (_voice->_snd[i])
- delete _voice->_snd[i];
- delete _voice->_env[i];
- }
- delete _voice;
-}
-
-void Towns_EuphonyPcmChannel::noteOn(byte note, byte onVelo) {
- _note = note;
- velocity(onVelo);
- _phase = 0;
-
- for (_current = 0; _current < 7; _current++) {
- if (note <= _voice->split[_current])
- break;
- }
-
- _voice->_env[_current]->state = s_attacking;
- _voice->_env[_current]->currentLevel = 0;
- _voice->_env[_current]->rate = _rate;
- _voice->_env[_current]->tickCount = 0;
-}
-
-void Towns_EuphonyPcmChannel::noteOff(byte note) {
- if (_current == -1)
- return;
- if (_voice->_env[_current]->state == s_ready)
- return;
-
- _voice->_env[_current]->state = s_releasing;
- _voice->_env[_current]->releaseLevel = _voice->_env[_current]->currentLevel;
- _voice->_env[_current]->tickCount = 0;
-}
-
-void Towns_EuphonyPcmChannel::controlChange(byte control, byte value) {
- switch (control) {
- case 0x07:
- // volume
- _ctrl7_volume = value;
- break;
- case 0x0A:
- // pan position
- break;
- case 0x79:
- // Reset controller
- for (uint8 i = 0; i < 8; i++) {
- if (_voice->_snd[i])
- delete _voice->_snd[i];
- delete _voice->_env[i];
- }
- delete _voice;
- _voice = new Voice;
- for (uint8 i = 0; i < 8; i++) {
- _voice->_env[i] = new Voice::Env;
- _voice->_snd[i] = 0;
- }
- break;
- case 0x7B:
- noteOff(_note);
- break;
- default:
- break;
- }
-}
-
-void Towns_EuphonyPcmChannel::sysEx_customInstrument(uint32 type, const byte *fmInst) {
- if (type == 0x80) {
- for (uint8 i = 0; i < 8; i++) {
- const byte * const *pos = (const byte * const *)fmInst;
- for (uint8 ii = 0; ii < 10; ii++) {
- if (_voice->id[i] == *(pos[ii] + 8)) {
- if (!_voice->_snd[i])
- _voice->_snd[i] = new Voice::Snd;
- memset(_voice->_snd[i]->name, 0, 9);
- memcpy(_voice->_snd[i]->name, (const char *)pos[ii], 8);
- _voice->_snd[i]->id = READ_LE_UINT32(pos[ii] + 8);
- _voice->_snd[i]->numSamples = READ_LE_UINT32(pos[ii] + 12);
- _voice->_snd[i]->loopStart = READ_LE_UINT32(pos[ii] + 16);
- _voice->_snd[i]->loopLength = READ_LE_UINT32(pos[ii] + 20);
- _voice->_snd[i]->samplingRate = READ_LE_UINT16(pos[ii] + 24);
- _voice->_snd[i]->keyOffset = READ_LE_UINT16(pos[ii] + 26);
- _voice->_snd[i]->keyNote = *(const uint8 *)(pos[ii] + 28);
- _voice->_snd[i]->_samples = (const int8 *)(pos[ii] + 32);
- }
- }
- }
- } else {
- memset(_voice->name, 0, 9);
- memcpy(_voice->name, (const char *)fmInst, 8);
-
- for (uint8 i = 0; i < 8; i++) {
- _voice->split[i] = READ_LE_UINT16(fmInst + 16 + 2 * i);
- _voice->id[i] = READ_LE_UINT32(fmInst + 32 + 4 * i);
- _voice->_snd[i] = 0;
- _voice->_env[i]->state = s_ready;
- _voice->_env[i]->currentLevel = 0;
- _voice->_env[i]->totalLevel = *(fmInst + 64 + 8 * i);
- _voice->_env[i]->attackRate = *(fmInst + 65 + 8 * i) * 10;
- _voice->_env[i]->decayRate = *(fmInst + 66 + 8 * i) * 10;
- _voice->_env[i]->sustainLevel = *(fmInst + 67 + 8 * i);
- _voice->_env[i]->sustainRate = *(fmInst + 68 + 8 * i) * 20;
- _voice->_env[i]->releaseRate = *(fmInst + 69 + 8 * i) * 10;
- _voice->_env[i]->rootKeyOffset = *(fmInst + 70 + 8 * i);
- }
- }
-}
-
-void Towns_EuphonyPcmChannel::pitchBend(int16 value) {
- _frequencyOffs = value;
-}
-
-void Towns_EuphonyPcmChannel::nextTick(int32 *outbuf, int buflen) {
- if (_current == -1 || !_voice->_snd[_current] || !_voice->_env[_current]->state || !_velocity) {
- velocity(0);
- _current = -1;
- return;
- }
-
- float phaseStep = SoundTowns::calculatePhaseStep(_note, _voice->_snd[_current]->keyNote -
- _voice->_env[_current]->rootKeyOffset, _voice->_snd[_current]->samplingRate, _rate, _frequencyOffs);
-
- int32 looplength = _voice->_snd[_current]->loopLength;
- int32 numsamples = _voice->_snd[_current]->numSamples;
- const int8 * samples = _voice->_snd[_current]->_samples;
-
- for (int i = 0; i < buflen; i++) {
- if (looplength > 0) {
- while (_phase >= numsamples)
- _phase -= looplength;
- } else {
- if (_phase >= numsamples) {
- velocity(0);
- _current = -1;
- break;
- }
- }
-
- int32 output;
-
- int32 phase0 = int32(_phase);
- int32 phase1 = int32(_phase + 1);
- if (phase1 >= numsamples)
- phase1 -= looplength;
- float weight0 = _phase - phase0;
- float weight1 = phase1 - _phase;
- output = int32(samples[phase0] * weight0 + samples[phase1] * weight1);
-
- output *= _velocity;
- output <<= 1;
-
- evpNextTick();
- output *= _voice->_env[_current]->currentLevel;
- output >>= 7;
- output *= _ctrl7_volume;
- output >>= 7;
-
- output *= 185;
- output >>= 8;
- outbuf[i] += output;
- _phase += phaseStep;
- }
-}
-
-void Towns_EuphonyPcmChannel::evpNextTick() {
- switch (_voice->_env[_current]->state) {
- case s_ready:
- _voice->_env[_current]->currentLevel = 0;
- return;
-
- case s_attacking:
- if (_voice->_env[_current]->attackRate == 0)
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
- else if (_voice->_env[_current]->attackRate >= 1270)
- _voice->_env[_current]->currentLevel = 0;
- else
- _voice->_env[_current]->currentLevel = (_voice->_env[_current]->totalLevel *
- _voice->_env[_current]->tickCount++ * 1000) /
- (_voice->_env[_current]->attackRate * _voice->_env[_current]->rate);
-
- if (_voice->_env[_current]->currentLevel >= _voice->_env[_current]->totalLevel) {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
- _voice->_env[_current]->state = s_decaying;
- _voice->_env[_current]->tickCount = 0;
- }
- break;
-
- case s_decaying:
- if (_voice->_env[_current]->decayRate == 0) {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
- } else if (_voice->_env[_current]->decayRate >= 1270) {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
- } else {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
- _voice->_env[_current]->currentLevel -= ((_voice->_env[_current]->totalLevel -
- _voice->_env[_current]->sustainLevel) * _voice->_env[_current]->tickCount++ * 1000) /
- (_voice->_env[_current]->decayRate * _voice->_env[_current]->rate);
- }
-
- if (_voice->_env[_current]->currentLevel <= _voice->_env[_current]->sustainLevel) {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
- _voice->_env[_current]->state = s_sustaining;
- _voice->_env[_current]->tickCount = 0;
- }
- break;
-
- case s_sustaining:
- if (_voice->_env[_current]->sustainRate == 0) {
- _voice->_env[_current]->currentLevel = 0;
- } else if (_voice->_env[_current]->sustainRate >= 2540) {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
- } else {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
- _voice->_env[_current]->currentLevel -= (_voice->_env[_current]->sustainLevel *
- _voice->_env[_current]->tickCount++ * 1000) / (_voice->_env[_current]->sustainRate *
- _voice->_env[_current]->rate);
- }
-
- if (_voice->_env[_current]->currentLevel <= 0) {
- _voice->_env[_current]->currentLevel = 0;
- _voice->_env[_current]->state = s_ready;
- _voice->_env[_current]->tickCount = 0;
- }
- break;
-
- case s_releasing:
- if (_voice->_env[_current]->releaseRate == 0) {
- _voice->_env[_current]->currentLevel = 0;
- } else if (_voice->_env[_current]->releaseRate >= 1270) {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->releaseLevel;
- } else {
- _voice->_env[_current]->currentLevel = _voice->_env[_current]->releaseLevel;
- _voice->_env[_current]->currentLevel -= (_voice->_env[_current]->releaseLevel *
- _voice->_env[_current]->tickCount++ * 1000) / (_voice->_env[_current]->releaseRate *
- _voice->_env[_current]->rate);
- }
-
- if (_voice->_env[_current]->currentLevel <= 0) {
- _voice->_env[_current]->currentLevel = 0;
- _voice->_env[_current]->state = s_ready;
- }
- break;
-
- default:
- break;
- }
-}
-
-void Towns_EuphonyPcmChannel::rate(uint16 r) {
- _rate = r;
-}
-
-void Towns_EuphonyPcmChannel::velocity(int velo) {
- _velocity = velo;
-}
-
-Towns_EuphonyDriver::Towns_EuphonyDriver(Audio::Mixer *mixer)
- : MidiDriver_Emulated(mixer) {
- _volume = 255;
- _fadestate = EUPHONY_FADEOUT_TICKS;
- _queue = 0;
-
- MidiDriver_YM2612::createLookupTables();
-
- for (uint8 i = 0; i < 6; i++)
- _channel[i] = _fChannel[i] = new Towns_EuphonyFmChannel;
- for (uint8 i = 0; i < 8; i++)
- _channel[i + 6] = _wChannel[i] = new Towns_EuphonyPcmChannel;
- _channel[14] = _channel[15] = 0;
-
- _fmInstruments = _waveInstruments = 0;
- memset(_waveSounds, 0, sizeof(uint8 *)* 10);
-
- rate(getRate());
- fading(0);
-
- _queue = new Towns_EuphonyTrackQueue(this, 0);
-}
-
-Towns_EuphonyDriver::~Towns_EuphonyDriver() {
- for (int i = 0; i < 6; i++)
- delete _fChannel[i];
- for (int i = 0; i < 8; i++)
- delete _wChannel[i];
-
- MidiDriver_YM2612::removeLookupTables();
-
- if (_fmInstruments) {
- delete[] _fmInstruments;
- _fmInstruments = 0;
- }
-
- if (_waveInstruments) {
- delete[] _waveInstruments;
- _waveInstruments = 0;
- }
-
- for (int i = 0; i < 10; i++) {
- if (_waveSounds[i]) {
- delete[] _waveSounds[i];
- _waveSounds[i] = 0;
- }
- }
-
- if (_queue) {
- _queue->release();
- delete _queue;
- _queue = 0;
- }
-}
-
-int Towns_EuphonyDriver::open() {
- if (_isOpen)
- return MERR_ALREADY_OPEN;
- MidiDriver_Emulated::open();
-
- _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle,
- this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
- return 0;
-}
-
-void Towns_EuphonyDriver::close() {
- if (!_isOpen)
- return;
- _isOpen = false;
- _mixer->stopHandle(_mixerSoundHandle);
-}
-
-void Towns_EuphonyDriver::send(uint32 b) {
- send(b & 0xF, b & 0xFFFFFFF0);
-}
-
-void Towns_EuphonyDriver::send(byte chan, uint32 b) {
- byte param2 = (byte) ((b >> 16) & 0xFF);
- byte param1 = (byte) ((b >> 8) & 0xFF);
- byte cmd = (byte) (b & 0xF0);
- if (chan > ARRAYSIZE(_channel))
- return;
-
- switch (cmd) {
- case 0x80:// Note Off
- if (_channel[chan])
- _channel[chan]->noteOff(param1);
- break;
- case 0x90: // Note On
- if (_channel[chan])
- _channel[chan]->noteOn(param1, param2);
- break;
- case 0xA0: // Aftertouch
- break; // Not supported.
- case 0xB0: // Control Change
- if (param1 == 0x79) {
- fading(0);
- for (int i = 0; i < 15; i++) {
- if (_channel[i]) {
- _channel[i]->controlChange(param1, param2);
- _channel[i]->programChange(0);
- }
- }
- } else if (param1 == 0x7B) {
- for (int i = 0; i < 15; i++) {
- if (_channel[i])
- _channel[i]->controlChange(param1, param2);
- }
- } else {
- if (_channel[chan])
- _channel[chan]->controlChange(param1, param2);
- }
- break;
- case 0xC0: // Program Change
- for (int i = 0; i < 6; i++) {
- if (_channel[chan] == _fChannel[i]) {
- _channel[chan]->sysEx_customInstrument(0, _fmInstruments + param1 * 0x30);
- break;
- }
- }
- for (int i = 0; i < 8; i++) {
- if (_channel[chan] == _wChannel[i]) {
- _channel[chan]->sysEx_customInstrument(0, _waveInstruments + param1 * 0x80);
- _channel[chan]->sysEx_customInstrument(0x80, (const byte *)_waveSounds);
- break;
- }
- }
- break;
- case 0xD0: // Channel Pressure
- break; // Not supported.
- case 0xE0: // Pitch Bend
- if (_channel[chan])
- _channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
- break;
- default:
- warning("Towns_EuphonyDriver: Unknown send() command 0x%02X", cmd);
- }
-}
-
-void Towns_EuphonyDriver::loadFmInstruments(const byte *instr) {
- delete[] _fmInstruments;
- _fmInstruments = new uint8[0x1800];
- memcpy(_fmInstruments, instr, 0x1800);
-}
-
-void Towns_EuphonyDriver::loadWaveInstruments(const byte *instr) {
- delete[] _waveInstruments;
- _waveInstruments = new uint8[0x1000];
- memcpy(_waveInstruments, instr, 0x1000);
-
- const uint8 *pos = (const uint8 *)(instr + 0x1000);
-
- for (uint8 i = 0; i < 10; i++) {
- delete[] _waveSounds[i];
- uint32 numsamples = READ_LE_UINT32(pos + 0x0C);
- _waveSounds[i] = new int8[numsamples + 0x20];
- memcpy(_waveSounds[i], pos, 0x20);
- pos += 0x20;
- for (uint32 ii = 0; ii < numsamples; ii++) {
- uint8 s = *(pos + ii);
- s = (s < 0x80) ? 0x80 - s : s;
- _waveSounds[i][ii + 0x20] = s ^ 0x80;
- }
- pos += numsamples;
- }
-}
-
-
-void Towns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) {
- _channel[midiChannelNumber] = _fChannel[fmChannelNumber];
-}
-
-void Towns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) {
- _channel[midiChannelNumber] = _wChannel[waveChannelNumber];
-}
-
-void Towns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) {
- _channel[midiChannelNumber] = 0;
-}
-
-void Towns_EuphonyDriver::generateSamples(int16 *data, int len) {
- memset(data, 0, 2 * sizeof(int16) * len);
- nextTick(data, len);
-}
-
-void Towns_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
- int32 *buf0 = (int32 *)buf1;
-
- for (int i = 0; i < ARRAYSIZE(_channel); i++) {
- if (_channel[i])
- _channel[i]->nextTick(buf0, buflen);
- }
-
- for (int i = 0; i < buflen; ++i) {
- int s = int( float(buf0[i] * _volume) * float((float)_fadestate / EUPHONY_FADEOUT_TICKS) );
- buf1[i*2] = buf1[i*2+1] = (s >> 9) & 0xffff;
- }
-
- if (_fading) {
- if (_fadestate) {
- _fadestate--;
- } else {
- _fading = false;
- _queue->setPlayBackStatus(false);
- }
- }
-}
-
-void Towns_EuphonyDriver::rate(uint16 r) {
- for (uint8 i = 0; i < 16; i++) {
- if (_channel[i])
- _channel[i]->rate(r);
- }
-}
-
-void Towns_EuphonyDriver::fading(bool status) {
- _fading = status;
- if (!_fading)
- _fadestate = EUPHONY_FADEOUT_TICKS;
-}
-
-Towns_EuphonyParser::Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue) : MidiParser(),
- _firstBaseTickStep(0x33), _nextBaseTickStep(0x33) {
- _initialTempo = calculateTempo(0x5a);
- _queue = queue;
-}
-
-void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
- byte *pos = _position._play_pos;
-
- if (_queue->_next) {
- if (info.ext.type == 0x2F) {
- unloadMusic();
- memset(&info, 0, sizeof(EventInfo));
- pos = _position._play_pos = _tracks[0] = _queue->trackData() + 0x806;
- } else if (_active_track == 255) {
- _queue = _queue->_next;
- setup();
- setTrack(0);
- _queue->setPlayBackStatus(true);
- return;
- } else if (!_queue->isPlaying()) {
- unloadMusic();
- _queue = _queue->_next;
- setup();
- setTrack(0);
- _queue->setPlayBackStatus(true);
- return;
- }
- }
-
- bool loop = true;
- while (loop) {
- byte cmd = *pos;
- byte evt = (cmd & 0xF0);
-
- if (evt == 0x90) {
- byte chan = pos[1];
-
- if (_enable[chan]) {
- uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
- info.start = pos + 6;
- uint32 last = _position._last_event_tick;
- info.delta = (tick < last) ? 0 : (tick - last);
-
- info.event = 0x90 | _channel[chan];
- info.length = pos[7] | (pos[8] << 4) | (pos[9] << 8) | (pos[10] << 12);
-
- int8 note = (int8) pos[4];
- if (_adjNote[chan]) {
- note = (note & 0x7f) & _adjNote[chan];
- if (note > 0x7c)
- note -= 0x0c;
- else if (note < 0)
- note += 0x0c;
- }
- info.basic.param1 = (byte) note;
-
- uint8 onVelo = (pos[5] & 0x7f) + _adjVelo[chan];
- if (onVelo > 0x7f)
- onVelo = 0x7f;
- if (onVelo < 1)
- onVelo = 1;
- info.basic.param2 = onVelo;
-
- pos += 12;
- loop = false;
- } else {
- pos += 6;
- }
- } else if (evt == 0xB0 || evt == 0xC0 || evt == 0xe0) {
- byte chan = pos[1];
-
- if (_enable[chan]) {
- info.start = pos;
- uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
- uint32 last = _position._last_event_tick;
- info.delta = (tick < last) ? 0 : (tick - last);
- info.event = evt | _channel[chan];
- info.length = 0;
- info.basic.param1 = pos[4];
- info.basic.param2 = pos[5];
- pos += 6;
- loop = false;
- } else {
- pos += 6;
- }
- } else if (cmd == 0xF2) {
- static const uint16 tickTable[] = { 0x180, 0xC0, 0x80, 0x60, 0x40, 0x30, 0x20, 0x18 };
- _baseTick += tickTable[_nextBaseTickStep >> 4] * ((_nextBaseTickStep & 0x0f) + 1);
- _nextBaseTickStep = pos[1];
- pos += 6;
- } else if (cmd == 0xF8) {
- int32 tempo = calculateTempo(pos[4] | (pos[5] << 7));
- info.event = 0xff;
- info.length = 3;
- info.ext.type = 0x51;
- _tempo[0] = (tempo >> 16) & 0xff;
- _tempo[1] = (tempo >> 8) & 0xff;
- _tempo[2] = tempo & 0xff;
- info.ext.data = (byte *)_tempo;
- pos += 6;
- loop = false;
- } else if (cmd == 0xFD || cmd == 0xFE) {
- // End of track.
- if (_autoLoop) {
- unloadMusic();
- _queue->setPlayBackStatus(true);
- pos = info.start = _tracks[0];
- } else {
- info.start = pos;
- }
-
- uint32 last = _position._last_event_tick;
- uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
- info.delta = (tick < last) ? 0 : (tick - last);
- info.event = 0xFF;
- info.ext.type = 0x2F;
- info.ext.data = pos;
- loop = false;
- } else {
- warning("Unknown Euphony music event 0x%02X", (int)cmd);
- memset(&info, 0, sizeof(info));
- pos = 0;
- loop = false;
- }
- }
- _position._play_pos = pos;
-}
-
-bool Towns_EuphonyParser::loadMusic(byte *data, uint32 size) {
- bool loop = _autoLoop;
-
- if (_queue->isPlaying() && !_queue->_loop) {
- _queue->loadDataToEndOfQueue(data, size, loop);
- } else {
- unloadMusic();
- _queue = _queue->release();
- _queue->loadDataToCurrentPosition(data, size, loop);
- setup();
- setTrack(0);
- _queue->setPlayBackStatus(true);
- }
- return true;
-}
-
-int32 Towns_EuphonyParser::calculateTempo(int16 val) {
- int32 tempo = val;
-
- if (tempo < 0)
- tempo = 0;
- if (tempo > 0x1F4)
- tempo = 0x1F4;
-
- tempo = 0x4C4B4 / (tempo + 0x1E);
- while (tempo < 0x451)
- tempo <<= 1;
- tempo <<= 8;
-
- return tempo;
-}
-
-void Towns_EuphonyParser::resetTracking() {
- MidiParser::resetTracking();
-
- _nextBaseTickStep = _firstBaseTickStep;
- _baseTick = 0;
- setTempo(_initialTempo);
- _queue->setPlayBackStatus(false);
-}
-
-void Towns_EuphonyParser::setup() {
- uint8 *data = _queue->trackData();
- if (!data)
- return;
- _queue->initDriver();
-
- _enable = data + 0x354;
- _mode = data + 0x374;
- _channel = data + 0x394;
- _adjVelo = data + 0x3B4;
- _adjNote = (int8 *)data + 0x3D4;
-
- _nextBaseTickStep = _firstBaseTickStep = data[0x804];
- _initialTempo = calculateTempo((data[0x805] > 0xfc) ? 0x5a : data[0x805]);
-
- property(MidiParser::mpAutoLoop, _queue->_loop);
-
- _num_tracks = 1;
- _ppqn = 120;
- _tracks[0] = data + 0x806;
-}
-
-Towns_EuphonyTrackQueue::Towns_EuphonyTrackQueue(Towns_EuphonyDriver * driver, Towns_EuphonyTrackQueue * last) {
- _trackData = 0;
- _next = 0;
- _driver = driver;
- _last = last;
- _used = _fchan = _wchan = 0;
- _playing = _loop = false;
-}
-
-void Towns_EuphonyTrackQueue::setPlayBackStatus(bool playing) {
- Towns_EuphonyTrackQueue *i = this;
- do {
- i->_playing = playing;
- i = i->_next;
- } while (i);
-}
-
-void Towns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) {
- delete[] _trackData;
- _trackData = new uint8[0xC58A];
- memset(_trackData, 0, 0xC58A);
- Screen::decodeFrame4(trackdata, _trackData, size);
-
- _used = _trackData + 0x374;
- _fchan = _trackData + 0x6d4;
- _wchan = _trackData + 0x6dA;
- _loop = loop;
- _playing = false;
-}
-
-void Towns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) {
- if (!_trackData) {
- loadDataToCurrentPosition(trackdata, size, loop);
- return;
- }
-
- Towns_EuphonyTrackQueue *i = this;
- while (i->_next)
- i = i->_next;
-
- i = i->_next = new Towns_EuphonyTrackQueue(_driver, i);
- i->_trackData = new uint8[0xC58A];
- memset(i->_trackData, 0, 0xC58A);
- Screen::decodeFrame4(trackdata, i->_trackData, size);
-
- i->_used = i->_trackData + 0x374;
- i->_fchan = i->_trackData + 0x6d4;
- i->_wchan = i->_trackData + 0x6dA;
- i->_loop = loop;
- i->_playing = _playing;
-}
-
-Towns_EuphonyTrackQueue *Towns_EuphonyTrackQueue::release() {
- Towns_EuphonyTrackQueue *i = this;
- while (i->_next)
- i = i->_next;
-
- Towns_EuphonyTrackQueue *res = i;
-
- while (i) {
- i->_playing = false;
- i->_used = i->_fchan = i->_wchan = 0;
- delete[] i->_trackData;
- i->_trackData = 0;
- i = i->_last;
- if (i) {
- res = i;
- delete i->_next;
- i->_next = 0;
- }
- }
-
- delete[] res->_trackData;
- res->_trackData = 0;
-
- return res;
-}
-
-void Towns_EuphonyTrackQueue::initDriver() {
- for (uint8 i = 0; i < 6; i++) {
- if (_used[_fchan[i]])
- _driver->assignFmChannel(_fchan[i], i);
- }
-
- for (uint8 i = 0; i < 8; i++) {
- if (_used[_wchan[i]])
- _driver->assignWaveChannel(_wchan[i], i);
- }
-
- for (uint8 i = 0; i < 16; i++) {
- if (!_used[i])
- _driver->removeChannel(i);
- }
- _driver->send(0x79B0);
-}
-
-class TownsPC98_OpnOperator {
-public:
- TownsPC98_OpnOperator(const uint32 timerbase, const uint8 *rateTable,
- const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
- const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);
- ~TownsPC98_OpnOperator() {}
-
- void keyOn();
- void keyOff();
- void frequency(int freq);
- void updatePhaseIncrement();
- void recalculateRates();
- void generateOutput(int32 phasebuf, int32 *feedbuf, int32 &out);
-
- void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; }
- void detune(int value) { _detn = &_detnTbl[value << 5]; }
- void multiple(uint32 value) { _multiple = value ? (value << 1) : 1; }
- void attackRate(uint32 value) { _specifiedAttackRate = value; }
- bool scaleRate(uint8 value);
- void decayRate(uint32 value) { _specifiedDecayRate = value; recalculateRates(); }
- void sustainRate(uint32 value) { _specifiedSustainRate = value; recalculateRates(); }
- void sustainLevel(uint32 value) { _sustainLevel = (value == 0x0f) ? 0x3e0 : value << 5; }
- void releaseRate(uint32 value) { _specifiedReleaseRate = value; recalculateRates(); }
- void totalLevel(uint32 value) { _totalLevel = value << 3; }
- void reset();
-
-protected:
- EnvelopeState _state;
- bool _playing;
- uint32 _feedbackLevel;
- uint32 _multiple;
- uint32 _totalLevel;
- uint8 _keyScale1;
- uint8 _keyScale2;
- uint32 _specifiedAttackRate;
- uint32 _specifiedDecayRate;
- uint32 _specifiedSustainRate;
- uint32 _specifiedReleaseRate;
- uint32 _tickCount;
- uint32 _sustainLevel;
-
- uint32 _frequency;
- uint8 _kcode;
- uint32 _phase;
- uint32 _phaseIncrement;
- const int32 *_detn;
-
- const uint8 *_rateTbl;
- const uint8 *_rshiftTbl;
- const uint8 *_adTbl;
- const uint32 *_fTbl;
- const uint32 *_sinTbl;
- const int32 *_tLvlTbl;
- const int32 *_detnTbl;
-
- const uint32 _tickLength;
- uint32 _timer;
- int32 _currentLevel;
-
- struct EvpState {
- uint8 rate;
- uint8 shift;
- } fs_a, fs_d, fs_s, fs_r;
-};
-
-TownsPC98_OpnOperator::TownsPC98_OpnOperator(const uint32 timerbase, const uint8 *rateTable,
- const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
- const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :
- _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable),
- _sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(timerbase * 2),
- _specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),
- _phase(0), _state(s_ready), _playing(false), _timer(0), _keyScale1(0), _keyScale2(0), _currentLevel(1023),
- _tickCount(0) {
-
- fs_a.rate = fs_a.shift = fs_d.rate = fs_d.shift = fs_s.rate = fs_s.shift = fs_r.rate = fs_r.shift = 0;
-
- reset();
-}
-
-void TownsPC98_OpnOperator::keyOn() {
- if (_playing)
- return;
-
- _playing = true;
- _state = s_attacking;
- _phase = 0;
-}
-
-void TownsPC98_OpnOperator::keyOff() {
- if (!_playing)
- return;
-
- _playing = false;
- if (_state != s_ready)
- _state = s_releasing;
-}
-
-void TownsPC98_OpnOperator::frequency(int freq) {
- uint8 block = (freq >> 11);
- uint16 pos = (freq & 0x7ff);
- uint8 c = pos >> 7;
-
- _kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6 ));
- _frequency = _fTbl[pos << 1] >> (7 - block);
-}
-
-void TownsPC98_OpnOperator::updatePhaseIncrement() {
- _phaseIncrement = ((_frequency + _detn[_kcode]) * _multiple) >> 1;
- uint8 keyscale = _kcode >> _keyScale1;
- if (_keyScale2 != keyscale) {
- _keyScale2 = keyscale;
- recalculateRates();
- }
-}
-
-void TownsPC98_OpnOperator::recalculateRates() {
- int k = _keyScale2;
- int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0;
- fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136;
- fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0;
-
- r = _specifiedDecayRate ? (_specifiedDecayRate << 1) + 0x20 : 0;
- fs_d.rate = _rateTbl[r + k];
- fs_d.shift = _rshiftTbl[r + k];
-
- r = _specifiedSustainRate ? (_specifiedSustainRate << 1) + 0x20 : 0;
- fs_s.rate = _rateTbl[r + k];
- fs_s.shift = _rshiftTbl[r + k];
-
- r = (_specifiedReleaseRate << 2) + 0x22;
- fs_r.rate = _rateTbl[r + k];
- fs_r.shift = _rshiftTbl[r + k];
-}
-
-void TownsPC98_OpnOperator::generateOutput(int32 phasebuf, int32 *feed, int32 &out) {
- if (_state == s_ready)
- return;
-
- _timer += _tickLength;
- while (_timer > 0x5B8D80) {
- _timer -= 0x5B8D80;
- ++_tickCount;
-
- int32 levelIncrement = 0;
- uint32 targetTime = 0;
- int32 targetLevel = 0;
- EnvelopeState nextState = s_ready;
-
- switch (_state) {
- case s_ready:
- return;
- case s_attacking:
- targetLevel = 0;
- nextState = s_decaying;
- if ((_specifiedAttackRate << 1) + _keyScale2 < 64) {
- targetTime = (1 << fs_a.shift) - 1;
- levelIncrement = (~_currentLevel * _adTbl[fs_a.rate + ((_tickCount >> fs_a.shift) & 7)]) >> 4;
- break;
- } else {
- _currentLevel = targetLevel;
- _state = nextState;
- }
- // Fall through
- case s_decaying:
- targetTime = (1 << fs_d.shift) - 1;
- nextState = s_sustaining;
- targetLevel = _sustainLevel;
- levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)];
- break;
- case s_sustaining:
- targetTime = (1 << fs_s.shift) - 1;
- nextState = s_sustaining;
- targetLevel = 1023;
- levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)];
- break;
- case s_releasing:
- targetTime = (1 << fs_r.shift) - 1;
- nextState = s_ready;
- targetLevel = 1023;
- levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)];
- break;
- }
-
- if (!(_tickCount & targetTime)) {
- _currentLevel += levelIncrement;
- if ((_state == s_attacking && _currentLevel <= targetLevel) || (_state != s_attacking && _currentLevel >= targetLevel)) {
- if (_state != s_decaying)
- _currentLevel = targetLevel;
- _state = nextState;
- }
- }
- }
-
- uint32 lvlout = _totalLevel + (uint32) _currentLevel;
-
-
- int32 outp = 0;
- int32 *i = &outp, *o = &outp;
- int phaseShift = 0;
-
- if (feed) {
- o = &feed[0];
- i = &feed[1];
- phaseShift = _feedbackLevel ? ((*o + *i) << _feedbackLevel) : 0;
- *o = *i;
- } else {
- phaseShift = phasebuf << 15;
- }
-
- if (lvlout < 832) {
- uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000)
- + phaseShift)) >> 16) & 0x3ff];
- *i = ((index < 6656) ? _tLvlTbl[index] : 0);
- } else {
- *i = 0;
- }
-
- _phase += _phaseIncrement;
- out += *o;
-}
-
-void TownsPC98_OpnOperator::reset(){
- keyOff();
- _timer = 0;
- _keyScale2 = 0;
- _currentLevel = 1023;
-
- frequency(0);
- detune(0);
- scaleRate(0);
- multiple(0);
- updatePhaseIncrement();
- attackRate(0);
- decayRate(0);
- releaseRate(0);
- sustainRate(0);
- feedbackLevel(0);
- totalLevel(127);
-}
-
-bool TownsPC98_OpnOperator::scaleRate(uint8 value) {
- value = 3 - value;
- if (_keyScale1 != value) {
- _keyScale1 = value;
- return true;
- }
-
- int k = _keyScale2;
- int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0;
- fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136;
- fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0;
- return false;
-}
-
-class TownsPC98_OpnDriver;
-class TownsPC98_OpnChannel {
-public:
- TownsPC98_OpnChannel(TownsPC98_OpnDriver *driver, uint8 regOffs, uint8 flgs, uint8 num,
- uint8 key, uint8 prt, uint8 id);
- virtual ~TownsPC98_OpnChannel();
- virtual void init();
-
- typedef enum channelState {
- CHS_RECALCFREQ = 0x01,
- CHS_KEYOFF = 0x02,
- CHS_SSGOFF = 0x04,
- CHS_VBROFF = 0x08,
- CHS_ALLOFF = 0x0f,
- CHS_PROTECT = 0x40,
- CHS_EOT = 0x80
- } ChannelState;
-
- virtual void loadData(uint8 *data);
- virtual void processEvents();
- virtual void processFrequency();
- virtual bool processControlEvent(uint8 cmd);
-
- virtual void keyOn();
- void keyOff();
-
- void setOutputLevel();
- virtual void fadeStep();
- void reset();
-
- const uint8 _idFlag;
-
-protected:
- void setupVibrato();
- bool processVibrato();
-
- bool control_dummy(uint8 para);
- bool control_f0_setPatch(uint8 para);
- bool control_f1_presetOutputLevel(uint8 para);
- bool control_f2_setKeyOffTime(uint8 para);
- bool control_f3_setFreqLSB(uint8 para);
- bool control_f4_setOutputLevel(uint8 para);
- bool control_f5_setTempo(uint8 para);
- bool control_f6_repeatSection(uint8 para);
- bool control_f7_setupVibrato(uint8 para);
- bool control_f8_toggleVibrato(uint8 para);
- bool control_fa_writeReg(uint8 para);
- virtual bool control_fb_incOutLevel(uint8 para);
- virtual bool control_fc_decOutLevel(uint8 para);
- bool control_fd_jump(uint8 para);
- virtual bool control_ff_endOfTrack(uint8 para);
-
- uint8 _ticksLeft;
- uint8 _algorithm;
- uint8 _instr;
- uint8 _totalLevel;
- uint8 _frqBlockMSB;
- int8 _frqLSB;
- uint8 _keyOffTime;
- bool _hold;
- uint8 *_dataPtr;
- uint8 _vbrInitDelayHi;
- uint8 _vbrInitDelayLo;
- int16 _vbrModInitVal;
- uint8 _vbrDuration;
- uint8 _vbrCurDelay;
- int16 _vbrModCurVal;
- uint8 _vbrDurLeft;
- uint16 _frequency;
- uint8 _block;
- uint8 _regOffset;
- uint8 _flags;
- uint8 _ssgTl;
- uint8 _ssgStep;
- uint8 _ssgTicksLeft;
- uint8 _ssgTargetLvl;
- uint8 _ssgStartLvl;
-
- const uint8 _chanNum;
- const uint8 _keyNum;
- const uint8 _part;
-
- TownsPC98_OpnDriver *_drv;
-
- typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para);
- const ControlEventFunc *controlEvents;
-};
-
-class TownsPC98_OpnChannelSSG : public TownsPC98_OpnChannel {
-public:
- TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,
- uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
- virtual ~TownsPC98_OpnChannelSSG() {}
- void init();
-
- virtual void loadData(uint8 *data);
- void processEvents();
- void processFrequency();
- bool processControlEvent(uint8 cmd);
-
- void keyOn();
- void nextShape();
-
- void protect();
- void restore();
-
- void fadeStep();
-
-protected:
- void setOutputLevel(uint8 lvl);
-
- bool control_f0_setInstr(uint8 para);
- bool control_f1_setTotalLevel(uint8 para);
- bool control_f4_setAlgorithm(uint8 para);
- bool control_f9_loadCustomPatch(uint8 para);
- bool control_fb_incOutLevel(uint8 para);
- bool control_fc_decOutLevel(uint8 para);
- bool control_ff_endOfTrack(uint8 para);
-
- typedef bool (TownsPC98_OpnChannelSSG::*ControlEventFunc)(uint8 para);
- const ControlEventFunc *controlEvents;
-};
-
-class TownsPC98_OpnSfxChannel : public TownsPC98_OpnChannelSSG {
-public:
- TownsPC98_OpnSfxChannel(TownsPC98_OpnDriver *driver, uint8 regOffs,
- uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
- TownsPC98_OpnChannelSSG(driver, regOffs, flgs, num, key, prt, id) {}
- ~TownsPC98_OpnSfxChannel() {}
-
- void loadData(uint8 *data);
-};
-
-class TownsPC98_OpnChannelPCM : public TownsPC98_OpnChannel {
-public:
- TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,
- uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id);
- ~TownsPC98_OpnChannelPCM() {}
- void init();
-
- void loadData(uint8 *data);
- void processEvents();
- bool processControlEvent(uint8 cmd);
-
-private:
- bool control_f1_prcStart(uint8 para);
- bool control_ff_endOfTrack(uint8 para);
-
- typedef bool (TownsPC98_OpnChannelPCM::*ControlEventFunc)(uint8 para);
- const ControlEventFunc *controlEvents;
-};
-
-class TownsPC98_OpnSquareSineSource {
-public:
- TownsPC98_OpnSquareSineSource(const uint32 timerbase);
- ~TownsPC98_OpnSquareSineSource();
-
- void init(const int *rsTable, const int *rseTable);
- void reset();
- void writeReg(uint8 address, uint8 value, bool force = false);
-
- void nextTick(int32 *buffer, uint32 bufferSize);
-
- uint8 chanEnable() const { return _chanEnable; }
-private:
- void updatesRegs();
-
- uint8 _updateRequestBuf[64];
- int _updateRequest;
- int _rand;
-
- int8 _evpTimer;
- uint32 _pReslt;
- uint8 _attack;
-
- bool _evpUpdate, _cont;
-
- int _evpUpdateCnt;
- uint8 _outN;
- int _nTick;
-
- int32 *_tlTable;
- int32 *_tleTable;
-
- const uint32 _tickLength;
- uint32 _timer;
-
- struct Channel {
- int tick;
- uint8 smp;
- uint8 out;
-
- uint8 frqL;
- uint8 frqH;
- uint8 vol;
- } _channels[3];
-
- uint8 _noiseGenerator;
- uint8 _chanEnable;
-
- uint8 **_reg;
-
- bool _ready;
-};
-
-class TownsPC98_OpnPercussionSource {
-public:
- TownsPC98_OpnPercussionSource(const uint32 timerbase);
- ~TownsPC98_OpnPercussionSource() { delete[] _reg; }
-
- void init(const uint8 *instrData = 0);
- void reset();
- void writeReg(uint8 address, uint8 value);
-
- void nextTick(int32 *buffer, uint32 bufferSize);
-
-private:
- struct RhtChannel {
- const uint8 *data;
-
- const uint8 *start;
- const uint8 *end;
- const uint8 *pos;
- uint32 size;
- bool active;
- uint8 level;
-
- int8 decState;
- uint8 decStep;
-
- int16 samples[2];
- int out;
-
- uint8 startPosH;
- uint8 startPosL;
- uint8 endPosH;
- uint8 endPosL;
- };
-
- void recalcOuput(RhtChannel *ins);
- void advanceInput(RhtChannel *ins);
-
- RhtChannel _rhChan[6];
-
- uint8 _totalLevel;
-
- const uint32 _tickLength;
- uint32 _timer;
-
- uint8 **_reg;
-
- bool _ready;
-};
-
-class TownsPC98_OpnCore : public Audio::AudioStream {
-public:
- enum OpnType {
- OD_TOWNS,
- OD_TYPE26,
- OD_TYPE86
- };
-
- TownsPC98_OpnCore(Audio::Mixer *mixer, OpnType type);
- virtual ~TownsPC98_OpnCore();
-
- virtual bool init();
- virtual void reset();
-
- void writeReg(uint8 part, uint8 regAddress, uint8 value);
-
- // AudioStream interface
- int readBuffer(int16 *buffer, const int numSamples);
- bool isStereo() const { return true; }
- bool endOfData() const { return false; }
- int getRate() const { return _mixer->getOutputRate(); }
-
-protected:
- void generateTables();
-
- void toggleRegProtection(bool prot) { _regProtectionFlag = prot; }
- uint8 readSSGStatus() { return _ssg->chanEnable(); }
-
- virtual void timerCallbackA() = 0;
- virtual void timerCallbackB() = 0;
-
- const int _numChan;
- const int _numSSG;
- const bool _hasPercussion;
-
- Common::Mutex _mutex;
-private:
- void nextTick(int32 *buffer, uint32 bufferSize);
- void generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed);
-
- struct ChanInternal {
- ChanInternal() {
- memset(this, 0, sizeof(ChanInternal));
- }
-
- ~ChanInternal() {
- for (uint i = 0; i < ARRAYSIZE(opr); ++i)
- delete opr[i];
- }
-
- uint16 frqTemp;
- bool enableLeft;
- bool enableRight;
- bool updateEnvelopeParameters;
- int32 feedbuf[3];
- uint8 algorithm;
- TownsPC98_OpnOperator *opr[4];
- };
-
- TownsPC98_OpnSquareSineSource *_ssg;
- TownsPC98_OpnPercussionSource *_prc;
- ChanInternal *_chanInternal;
-
- uint8 *_oprRates;
- uint8 *_oprRateshift;
- uint8 *_oprAttackDecay;
- uint32 *_oprFrq;
- uint32 *_oprSinTbl;
- int32 *_oprLevelOut;
- int32 *_oprDetune;
-
- bool _regProtectionFlag;
-
- typedef void (TownsPC98_OpnCore::*OpnTimerProc)();
-
- struct OpnTimer {
- bool enabled;
- uint16 value;
-
- int32 smpTillCb;
- uint32 smpTillCbRem;
- int32 smpPerCb;
- uint32 smpPerCbRem;
-
- OpnTimerProc cb;
- };
-
- OpnTimer _timers[2];
-
- const float _baserate;
- uint32 _timerbase;
-
- Audio::Mixer *_mixer;
- Audio::SoundHandle _soundHandle;
-
- static const uint8 _percussionData[];
- static const uint32 _adtStat[];
- static const uint8 _detSrc[];
- static const int _ssgTables[];
-
- bool _ready;
-};
-
-class TownsPC98_OpnDriver : public TownsPC98_OpnCore {
-friend class TownsPC98_OpnChannel;
-friend class TownsPC98_OpnChannelSSG;
-friend class TownsPC98_OpnSfxChannel;
-friend class TownsPC98_OpnChannelPCM;
-public:
- TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type);
- ~TownsPC98_OpnDriver();
-
- void loadMusicData(uint8 *data, bool loadPaused = false);
- void loadSoundEffectData(uint8 *data, uint8 trackNum);
- bool init();
- void reset();
-
- void fadeStep();
-
- void pause() { _musicPlaying = false; }
- void cont() { _musicPlaying = true; }
-
- void timerCallbackB();
- void timerCallbackA();
-
- bool looping() { return _looping == _updateChannelsFlag ? true : false; }
- bool musicPlaying() { return _musicPlaying; }
-
-protected:
- void startSoundEffect();
-
- void setMusicTempo(uint8 tempo);
- void setSfxTempo(uint16 tempo);
-
- TownsPC98_OpnChannel **_channels;
- TownsPC98_OpnChannelSSG **_ssgChannels;
- TownsPC98_OpnSfxChannel **_sfxChannels;
- TownsPC98_OpnChannelPCM *_rhythmChannel;
-
- const uint8 *_opnCarrier;
- const uint8 *_opnFreqTable;
- const uint8 *_opnFreqTableSSG;
- const uint8 *_opnFxCmdLen;
- const uint8 *_opnLvlPresets;
-
- uint8 *_musicBuffer;
- uint8 *_sfxBuffer;
- uint8 *_trackPtr;
- uint8 *_patches;
- uint8 *_ssgPatches;
-
- uint8 _updateChannelsFlag;
- uint8 _updateSSGFlag;
- uint8 _updateRhythmFlag;
- uint8 _updateSfxFlag;
- uint8 _finishedChannelsFlag;
- uint8 _finishedSSGFlag;
- uint8 _finishedRhythmFlag;
- uint8 _finishedSfxFlag;
-
- bool _musicPlaying;
- bool _sfxPlaying;
- uint8 _fading;
- uint8 _looping;
- uint32 _musicTickCounter;
-
- int _sfxOffs;
- uint8 *_sfxData;
- uint16 _sfxOffsets[2];
-
- static const uint8 _drvTables[];
-
- bool _ready;
-};
-
-TownsPC98_OpnChannel::TownsPC98_OpnChannel(TownsPC98_OpnDriver *driver, uint8 regOffs, uint8 flgs, uint8 num,
- uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key),
- _part(prt), _idFlag(id), controlEvents(0) {
-
- _ticksLeft = _algorithm = _instr = _totalLevel = _frqBlockMSB = _keyOffTime = 0;
- _ssgStartLvl = _ssgTl = _ssgStep = _ssgTicksLeft = _ssgTargetLvl = _block = 0;
- _vbrInitDelayHi = _vbrInitDelayLo = _vbrDuration = _vbrCurDelay = _vbrDurLeft = 0;
- _frqLSB = 0;
- _hold = false;
- _dataPtr = 0;
- _vbrModInitVal = _vbrModCurVal = 0;
- _frequency = 0;
-}
-
-TownsPC98_OpnChannel::~TownsPC98_OpnChannel() {
-}
-
-void TownsPC98_OpnChannel::init() {
- #define Control(x) &TownsPC98_OpnChannel::control_##x
- static const ControlEventFunc ctrlEvents[] = {
- Control(f0_setPatch),
- Control(f1_presetOutputLevel),
- Control(f2_setKeyOffTime),
- Control(f3_setFreqLSB),
- Control(f4_setOutputLevel),
- Control(f5_setTempo),
- Control(f6_repeatSection),
- Control(f7_setupVibrato),
- Control(f8_toggleVibrato),
- Control(dummy),
- Control(fa_writeReg),
- Control(fb_incOutLevel),
- Control(fc_decOutLevel),
- Control(fd_jump),
- Control(dummy),
- Control(ff_endOfTrack)
- };
- #undef Control
-
- controlEvents = ctrlEvents;
-}
-
-void TownsPC98_OpnChannel::keyOff() {
- // all operators off
- uint8 value = _keyNum & 0x0f;
- if (_part)
- value |= 4;
- uint8 regAddress = 0x28;
- _drv->writeReg(0, regAddress, value);
- _flags |= CHS_KEYOFF;
-}
-
-void TownsPC98_OpnChannel::keyOn() {
- // all operators on
- uint8 value = _keyNum | 0xf0;
- if (_part)
- value |= 4;
- uint8 regAddress = 0x28;
- _drv->writeReg(0, regAddress, value);
-}
-
-void TownsPC98_OpnChannel::loadData(uint8 *data) {
- _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF;
- _ticksLeft = 1;
- _dataPtr = data;
- _totalLevel = 0x7F;
-
- uint8 *src_b = _dataPtr;
- int loop = 1;
- uint8 cmd = 0;
- while (loop) {
- if (loop == 1) {
- cmd = *src_b++;
- if (cmd < 0xf0) {
- src_b++;
- loop = 1;
- } else {
- if (cmd == 0xff) {
- loop = *src_b ? 2 : 0;
- if (READ_LE_UINT16(src_b))
- _drv->_looping |= _idFlag;
- } else if (cmd == 0xf6) {
- loop = 3;
- } else {
- loop = 2;
- }
- }
- } else if (loop == 2) {
- src_b += _drv->_opnFxCmdLen[cmd - 240];
- loop = 1;
- } else if (loop == 3) {
- src_b[0] = src_b[1];
- src_b += 4;
- loop = 1;
- }
- }
-}
-
-void TownsPC98_OpnChannel::processEvents() {
- if (_flags & CHS_EOT)
- return;
-
- if (!_hold && _ticksLeft == _keyOffTime)
- keyOff();
-
- if (--_ticksLeft)
- return;
-
- if (!_hold)
- keyOff();
-
- uint8 cmd = 0;
- bool loop = true;
-
- while (loop) {
- cmd = *_dataPtr++;
- if (cmd < 0xf0)
- loop = false;
- else if (!processControlEvent(cmd))
- return;
- }
-
- uint8 para = *_dataPtr++;
-
- if (cmd == 0x80) {
- keyOff();
- _hold = false;
- } else {
- keyOn();
-
- if (_hold == false || cmd != _frqBlockMSB)
- _flags |= CHS_RECALCFREQ;
-
- _hold = (para & 0x80) ? true : false;
- _frqBlockMSB = cmd;
- }
-
- _ticksLeft = para & 0x7f;
-}
-
-void TownsPC98_OpnChannel::processFrequency() {
- if (_flags & CHS_RECALCFREQ) {
-
- _frequency = (((const uint16 *)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f] + _frqLSB) | (((_frqBlockMSB & 0x70) >> 1) << 8);
-
- _drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8));
- _drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff));
-
- setupVibrato();
- }
-
- if (!(_flags & CHS_VBROFF)) {
- if (!processVibrato())
- return;
-
- _drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8));
- _drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff));
- }
-}
-
-void TownsPC98_OpnChannel::setupVibrato() {
- _vbrCurDelay = _vbrInitDelayHi;
- if (_flags & CHS_KEYOFF) {
- _vbrModCurVal = _vbrModInitVal;
- _vbrCurDelay += _vbrInitDelayLo;
- }
- _vbrDurLeft = (_vbrDuration >> 1);
- _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
-}
-
-bool TownsPC98_OpnChannel::processVibrato() {
- if (--_vbrCurDelay)
- return false;
-
- _vbrCurDelay = _vbrInitDelayHi;
- _frequency += _vbrModCurVal;
-
- if (!--_vbrDurLeft) {
- _vbrDurLeft = _vbrDuration;
- _vbrModCurVal = -_vbrModCurVal;
- }
-
- return true;
-}
-
-bool TownsPC98_OpnChannel::processControlEvent(uint8 cmd) {
- uint8 para = *_dataPtr++;
- return (this->*controlEvents[cmd & 0x0f])(para);
-}
-
-void TownsPC98_OpnChannel::setOutputLevel() {
- uint8 outopr = _drv->_opnCarrier[_algorithm];
- uint8 reg = 0x40 + _regOffset;
-
- for (int i = 0; i < 4; i++) {
- if (outopr & 1)
- _drv->writeReg(_part, reg, _totalLevel);
- outopr >>= 1;
- reg += 4;
- }
-}
-
-void TownsPC98_OpnChannel::fadeStep() {
- _totalLevel += 3;
- if (_totalLevel > 0x7f)
- _totalLevel = 0x7f;
- setOutputLevel();
-}
-
-void TownsPC98_OpnChannel::reset() {
- _hold = false;
- _keyOffTime = 0;
- _ticksLeft = 1;
-
- _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF;
-
- _totalLevel = 0;
- _algorithm = 0;
- _flags = CHS_EOT;
- _algorithm = 0;
-
- _block = 0;
- _frequency = 0;
- _frqBlockMSB = 0;
- _frqLSB = 0;
-
- _ssgTl = 0;
- _ssgStartLvl = 0;
- _ssgTargetLvl = 0;
- _ssgStep = 0;
- _ssgTicksLeft = 0;
-
- _vbrInitDelayHi = 0;
- _vbrInitDelayLo = 0;
- _vbrModInitVal = 0;
- _vbrDuration = 0;
- _vbrCurDelay = 0;
- _vbrModCurVal = 0;
- _vbrDurLeft = 0;
-}
-
-bool TownsPC98_OpnChannel::control_f0_setPatch(uint8 para) {
- _instr = para;
- uint8 reg = _regOffset + 0x80;
-
- for (int i = 0; i < 4; i++) {
- // set release rate for each operator
- _drv->writeReg(_part, reg, 0x0f);
- reg += 4;
- }
-
- const uint8 *tptr = _drv->_patches + ((uint32)_instr << 5);
- reg = _regOffset + 0x30;
-
- // write registers 0x30 to 0x8f
- for (int i = 0; i < 6; i++) {
- _drv->writeReg(_part, reg, tptr[0]);
- reg += 4;
- _drv->writeReg(_part, reg, tptr[2]);
- reg += 4;
- _drv->writeReg(_part, reg, tptr[1]);
- reg += 4;
- _drv->writeReg(_part, reg, tptr[3]);
- reg += 4;
- tptr += 4;
- }
-
- reg = _regOffset + 0xB0;
- _algorithm = tptr[0] & 7;
- // set feedback and algorithm
- _drv->writeReg(_part, reg, tptr[0]);
-
- setOutputLevel();
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f1_presetOutputLevel(uint8 para) {
- if (_drv->_fading)
- return true;
-
- _totalLevel = _drv->_opnLvlPresets[para];
- setOutputLevel();
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f2_setKeyOffTime(uint8 para) {
- _keyOffTime = para;
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f3_setFreqLSB(uint8 para) {
- _frqLSB = (int8) para;
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f4_setOutputLevel(uint8 para) {
- if (_drv->_fading)
- return true;
-
- _totalLevel = para;
- setOutputLevel();
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f5_setTempo(uint8 para) {
- _drv->setMusicTempo(para);
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f6_repeatSection(uint8 para) {
- _dataPtr--;
- _dataPtr[0]--;
-
- if (*_dataPtr) {
- // repeat section until counter has reached zero
- _dataPtr = _drv->_trackPtr + READ_LE_UINT16(_dataPtr + 2);
- } else {
- // reset counter, advance to next section
- _dataPtr[0] = _dataPtr[1];
- _dataPtr += 4;
- }
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f7_setupVibrato(uint8 para) {
- _vbrInitDelayHi = _dataPtr[0];
- _vbrInitDelayLo = para;
- _vbrModInitVal = (int16) READ_LE_UINT16(_dataPtr + 1);
- _vbrDuration = _dataPtr[3];
- _dataPtr += 4;
- _flags = (_flags & ~CHS_VBROFF) | CHS_KEYOFF | CHS_RECALCFREQ;
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_f8_toggleVibrato(uint8 para) {
- if (para == 0x10) {
- if (*_dataPtr++) {
- _flags = (_flags & ~CHS_VBROFF) | CHS_KEYOFF;
- } else {
- _flags |= CHS_VBROFF;
- }
- } else {
- /* NOT IMPLEMENTED
- uint8 skipChannels = para / 36;
- uint8 entry = para % 36;
- TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels];
-
- t->unnamedEntries[entry] = *_dataPtr++;*/
- }
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_fa_writeReg(uint8 para) {
- _drv->writeReg(_part, para, *_dataPtr++);
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_fb_incOutLevel(uint8 para) {
- _dataPtr--;
- if (_drv->_fading)
- return true;
-
- uint8 val = (_totalLevel + 3);
- if (val > 0x7f)
- val = 0x7f;
-
- _totalLevel = val;
- setOutputLevel();
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_fc_decOutLevel(uint8 para) {
- _dataPtr--;
- if (_drv->_fading)
- return true;
-
- int8 val = (int8) (_totalLevel - 3);
- if (val < 0)
- val = 0;
-
- _totalLevel = (uint8) val;
- setOutputLevel();
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_fd_jump(uint8 para) {
- uint8 *tmp = _drv->_trackPtr + READ_LE_UINT16(_dataPtr - 1);
- _dataPtr = (tmp[1] == 1) ? tmp : (_dataPtr + 1);
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_dummy(uint8 para) {
- _dataPtr--;
- return true;
-}
-
-bool TownsPC98_OpnChannel::control_ff_endOfTrack(uint8 para) {
- uint16 val = READ_LE_UINT16(--_dataPtr);
- if (val) {
- // loop
- _dataPtr = _drv->_trackPtr + val;
- return true;
- } else {
- // quit parsing for active channel
- --_dataPtr;
- _flags |= CHS_EOT;
- _drv->_finishedChannelsFlag |= _idFlag;
- keyOff();
- return false;
- }
-}
-
-TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs,
- uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
- TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) {
-}
-
-void TownsPC98_OpnChannelSSG::init() {
- _algorithm = 0x80;
-
- #define Control(x) &TownsPC98_OpnChannelSSG::control_##x
- static const ControlEventFunc ctrlEventsSSG[] = {
- Control(f0_setInstr),
- Control(f1_setTotalLevel),
- Control(f2_setKeyOffTime),
- Control(f3_setFreqLSB),
- Control(f4_setAlgorithm),
- Control(f5_setTempo),
- Control(f6_repeatSection),
- Control(f7_setupVibrato),
- Control(f8_toggleVibrato),
- Control(f9_loadCustomPatch),
- Control(fa_writeReg),
- Control(fb_incOutLevel),
- Control(fc_decOutLevel),
- Control(fd_jump),
- Control(dummy),
- Control(ff_endOfTrack)
- };
- #undef Control
-
- controlEvents = ctrlEventsSSG;
-}
-
-void TownsPC98_OpnChannelSSG::processEvents() {
- if (_flags & CHS_EOT)
- return;
-
- _drv->toggleRegProtection(_flags & CHS_PROTECT ? true : false);
-
- if (!_hold && _ticksLeft == _keyOffTime)
- nextShape();
-
- if (!--_ticksLeft) {
-
- uint8 cmd = 0;
- bool loop = true;
-
- while (loop) {
- cmd = *_dataPtr++;
- if (cmd < 0xf0)
- loop = false;
- else if (!processControlEvent(cmd))
- return;
- }
-
- uint8 para = *_dataPtr++;
-
- if (cmd == 0x80) {
- nextShape();
- _hold = false;
- } else {
- if (!_hold) {
- _instr &= 0xf0;
- _ssgStep = _drv->_ssgPatches[_instr];
- _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
- _ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
- _ssgStartLvl = _drv->_ssgPatches[_instr + 3];
- _flags = (_flags & ~CHS_SSGOFF) | CHS_KEYOFF;
- }
-
- keyOn();
-
- if (_hold == false || cmd != _frqBlockMSB)
- _flags |= CHS_RECALCFREQ;
-
- _hold = (para & 0x80) ? true : false;
- _frqBlockMSB = cmd;
- }
-
- _ticksLeft = para & 0x7f;
- }
-
- if (!(_flags & CHS_SSGOFF)) {
- if (--_ssgTicksLeft) {
- if (!_drv->_fading)
- setOutputLevel(_ssgStartLvl);
- return;
- }
-
- _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
-
- if (_drv->_ssgPatches[_instr + 1] & 0x80) {
- uint8 t = _ssgStartLvl - _ssgStep;
-
- if (_ssgStep <= _ssgStartLvl && _ssgTargetLvl < t) {
- if (!_drv->_fading)
- setOutputLevel(t);
- return;
- }
- } else {
- int t = _ssgStartLvl + _ssgStep;
- uint8 p = (uint8) (t & 0xff);
-
- if (t < 256 && _ssgTargetLvl > p) {
- if (!_drv->_fading)
- setOutputLevel(p);
- return;
- }
- }
-
- setOutputLevel(_ssgTargetLvl);
- if (_ssgStartLvl && !(_instr & 8)){
- _instr += 4;
- _ssgStep = _drv->_ssgPatches[_instr];
- _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
- _ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
- } else {
- _flags |= CHS_SSGOFF;
- setOutputLevel(0);
- }
- }
-}
-
-void TownsPC98_OpnChannelSSG::processFrequency() {
- if (_algorithm & 0x40)
- return;
-
- if (_flags & CHS_RECALCFREQ) {
- _block = _frqBlockMSB >> 4;
- _frequency = ((const uint16 *)_drv->_opnFreqTableSSG)[_frqBlockMSB & 0x0f] + _frqLSB;
-
- uint16 f = _frequency >> _block;
- _drv->writeReg(_part, _regOffset << 1, f & 0xff);
- _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8);
-
- setupVibrato();
- }
-
- if (!(_flags & (CHS_EOT | CHS_VBROFF | CHS_SSGOFF))) {
- if (!processVibrato())
- return;
-
- uint16 f = _frequency >> _block;
- _drv->writeReg(_part, _regOffset << 1, f & 0xff);
- _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8);
- }
-}
-
-bool TownsPC98_OpnChannelSSG::processControlEvent(uint8 cmd) {
- uint8 para = *_dataPtr++;
- return (this->*controlEvents[cmd & 0x0f])(para);
-}
-
-void TownsPC98_OpnChannelSSG::nextShape() {
- _instr = (_instr & 0xf0) + 0x0c;
- _ssgStep = _drv->_ssgPatches[_instr];
- _ssgTicksLeft = _drv->_ssgPatches[_instr + 1] & 0x7f;
- _ssgTargetLvl = _drv->_ssgPatches[_instr + 2];
-}
-
-void TownsPC98_OpnChannelSSG::keyOn() {
- uint8 c = 0x7b;
- uint8 t = (_algorithm & 0xC0) << 1;
- if (_algorithm & 0x80)
- t |= 4;
-
- c = (c << (_regOffset + 1)) | (c >> (7 - _regOffset));
- t = (t << (_regOffset + 1)) | (t >> (7 - _regOffset));
-
- if (!(_algorithm & 0x80))
- _drv->writeReg(_part, 6, _algorithm & 0x7f);
-
- uint8 e = (_drv->readSSGStatus() & c) | t;
- _drv->writeReg(_part, 7, e);
-}
-
-void TownsPC98_OpnChannelSSG::protect() {
- _flags |= CHS_PROTECT;
-}
-
-void TownsPC98_OpnChannelSSG::restore() {
- _flags &= ~CHS_PROTECT;
- keyOn();
- _drv->writeReg(_part, 8 + _regOffset, _ssgTl);
- uint16 f = _frequency >> _block;
- _drv->writeReg(_part, _regOffset << 1, f & 0xff);
- _drv->writeReg(_part, (_regOffset << 1) + 1, f >> 8);
-}
-
-void TownsPC98_OpnChannelSSG::loadData(uint8 *data) {
- _drv->toggleRegProtection(_flags & CHS_PROTECT ? true : false);
- TownsPC98_OpnChannel::loadData(data);
- setOutputLevel(0);
- _algorithm = 0x80;
-}
-
-void TownsPC98_OpnChannelSSG::setOutputLevel(uint8 lvl) {
- _ssgStartLvl = lvl;
- uint16 newTl = (((uint16)_totalLevel + 1) * (uint16)lvl) >> 8;
- if (newTl == _ssgTl)
- return;
- _ssgTl = newTl;
- _drv->writeReg(_part, 8 + _regOffset, _ssgTl);
-}
-
-void TownsPC98_OpnChannelSSG::fadeStep() {
- _totalLevel--;
- if ((int8)_totalLevel < 0)
- _totalLevel = 0;
- setOutputLevel(_ssgStartLvl);
-}
-
-bool TownsPC98_OpnChannelSSG::control_f0_setInstr(uint8 para) {
- _instr = para << 4;
- para = (para >> 3) & 0x1e;
- if (para)
- return control_f4_setAlgorithm(para | 0x40);
- return true;
-}
-
-bool TownsPC98_OpnChannelSSG::control_f1_setTotalLevel(uint8 para) {
- if (!_drv->_fading)
- _totalLevel = para;
- return true;
-}
-
-bool TownsPC98_OpnChannelSSG::control_f4_setAlgorithm(uint8 para) {
- _algorithm = para;
- return true;
-}
-
-bool TownsPC98_OpnChannelSSG::control_f9_loadCustomPatch(uint8 para) {
- _instr = (_drv->_sfxOffs + 10 + _regOffset) << 4;
- _drv->_ssgPatches[_instr] = *_dataPtr++;
- _drv->_ssgPatches[_instr + 3] = para;
- _drv->_ssgPatches[_instr + 4] = *_dataPtr++;
- _drv->_ssgPatches[_instr + 6] = *_dataPtr++;
- _drv->_ssgPatches[_instr + 8] = *_dataPtr++;
- _drv->_ssgPatches[_instr + 12] = *_dataPtr++;
- return true;
-}
-
-bool TownsPC98_OpnChannelSSG::control_fb_incOutLevel(uint8 para) {
- _dataPtr--;
- if (_drv->_fading)
- return true;
-
- _totalLevel--;
- if ((int8)_totalLevel < 0)
- _totalLevel = 0;
-
- return true;
-}
-
-bool TownsPC98_OpnChannelSSG::control_fc_decOutLevel(uint8 para) {
- _dataPtr--;
- if (_drv->_fading)
- return true;
-
- if (_totalLevel + 1 < 0x10)
- _totalLevel++;
-
- return true;
-}
-
-bool TownsPC98_OpnChannelSSG::control_ff_endOfTrack(uint8 para) {
- if (!_drv->_sfxOffs) {
- uint16 val = READ_LE_UINT16(--_dataPtr);
- if (val) {
- // loop
- _dataPtr = _drv->_trackPtr + val;
- return true;
- } else {
- // stop parsing
- if (!_drv->_fading)
- setOutputLevel(0);
- --_dataPtr;
- _flags |= CHS_EOT;
- _drv->_finishedSSGFlag |= _idFlag;
- }
- } else {
- // end of sfx track - restore ssg music channel
- _flags |= CHS_EOT;
- _drv->_finishedSfxFlag |= _idFlag;
- _drv->_ssgChannels[_chanNum]->restore();
- }
-
- return false;
-}
-
-void TownsPC98_OpnSfxChannel::loadData(uint8 *data) {
- _flags = CHS_ALLOFF;
- _ticksLeft = 1;
- _dataPtr = data;
- _ssgTl = 0xff;
- _algorithm = 0x80;
-}
-
-TownsPC98_OpnChannelPCM::TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,
- uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
- TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) {
-}
-
-void TownsPC98_OpnChannelPCM::init() {
- _algorithm = 0x80;
-
- #define Control(x) &TownsPC98_OpnChannelPCM::control_##x
- static const ControlEventFunc ctrlEventsPCM[] = {
- Control(dummy),
- Control(f1_prcStart),
- Control(dummy),
- Control(dummy),
- Control(dummy),
- Control(dummy),
- Control(f6_repeatSection),
- Control(dummy),
- Control(dummy),
- Control(dummy),
- Control(fa_writeReg),
- Control(dummy),
- Control(dummy),
- Control(dummy),
- Control(dummy),
- Control(ff_endOfTrack)
- };
- #undef Control
-
- controlEvents = ctrlEventsPCM;
-}
-
-void TownsPC98_OpnChannelPCM::loadData(uint8 *data) {
- _flags = (_flags & ~CHS_EOT) | CHS_ALLOFF;
- _ticksLeft = 1;
- _dataPtr = data;
- _totalLevel = 0x7F;
-}
-
-void TownsPC98_OpnChannelPCM::processEvents() {
- if (_flags & CHS_EOT)
- return;
-
- if (--_ticksLeft)
- return;
-
- uint8 cmd = 0;
- bool loop = true;
-
- while (loop) {
- cmd = *_dataPtr++;
- if (cmd == 0x80) {
- loop = false;
- } else if (cmd < 0xf0) {
- _drv->writeReg(_part, 0x10, cmd);
- } else if (!processControlEvent(cmd)) {
- return;
- }
- }
-
- _ticksLeft = *_dataPtr++;
-}
-
-bool TownsPC98_OpnChannelPCM::processControlEvent(uint8 cmd) {
- uint8 para = *_dataPtr++;
- return (this->*controlEvents[cmd & 0x0f])(para);
-}
-
-bool TownsPC98_OpnChannelPCM::control_f1_prcStart(uint8 para) {
- _totalLevel = para;
- _drv->writeReg(_part, 0x11, para);
- return true;
-}
-
-bool TownsPC98_OpnChannelPCM::control_ff_endOfTrack(uint8 para) {
- uint16 val = READ_LE_UINT16(--_dataPtr);
- if (val) {
- // loop
- _dataPtr = _drv->_trackPtr + val;
- return true;
- } else {
- // quit parsing for active channel
- --_dataPtr;
- _flags |= CHS_EOT;
- _drv->_finishedRhythmFlag |= _idFlag;
- return false;
- }
-}
-
-TownsPC98_OpnSquareSineSource::TownsPC98_OpnSquareSineSource(const uint32 timerbase) : _tlTable(0),
- _tleTable(0), _updateRequest(-1), _tickLength(timerbase * 27), _ready(0), _reg(0), _rand(1), _outN(1),
- _nTick(0), _evpUpdateCnt(0), _evpTimer(0x1f), _pReslt(0x1f), _attack(0), _cont(false), _evpUpdate(true),
- _timer(0), _noiseGenerator(0), _chanEnable(0) {
-
- memset(_channels, 0, sizeof(_channels));
- memset(_updateRequestBuf, 0, sizeof(_updateRequestBuf));
- _reg = new uint8 *[11];
-
- _reg[0] = &_channels[0].frqL;
- _reg[1] = &_channels[0].frqH;
- _reg[2] = &_channels[1].frqL;
- _reg[3] = &_channels[1].frqH;
- _reg[4] = &_channels[2].frqL;
- _reg[5] = &_channels[2].frqH;
- _reg[6] = &_noiseGenerator;
- _reg[7] = &_chanEnable;
- _reg[8] = &_channels[0].vol;
- _reg[9] = &_channels[1].vol;
- _reg[10] = &_channels[2].vol;
-
- reset();
-}
-
-TownsPC98_OpnSquareSineSource::~TownsPC98_OpnSquareSineSource() {
- delete[] _tlTable;
- delete[] _tleTable;
- delete[] _reg;
-}
-
-void TownsPC98_OpnSquareSineSource::init(const int *rsTable, const int *rseTable) {
- if (_ready) {
- reset();
- return;
- }
-
- delete[] _tlTable;
- delete[] _tleTable;
- _tlTable = new int32[16];
- _tleTable = new int32[32];
- float a, b, d;
- d = 801.0f;
-
- for (int i = 0; i < 16; i++) {
- b = 1.0f / rsTable[i];
- a = 1.0f / d + b + 1.0f / 1000.0f;
- float v = (b / a) * 32767.0f;
- _tlTable[i] = (int32) v;
-
- b = 1.0f / rseTable[i];
- a = 1.0f / d + b + 1.0f / 1000.0f;
- v = (b / a) * 32767.0f;
- _tleTable[i] = (int32) v;
- }
-
- for (int i = 16; i < 32; i++) {
- b = 1.0f / rseTable[i];
- a = 1.0f / d + b + 1.0f / 1000.0f;
- float v = (b / a) * 32767.0f;
- _tleTable[i] = (int32) v;
- }
-
- _ready = true;
-}
-
-void TownsPC98_OpnSquareSineSource::reset() {
- _rand = 1;
- _outN = 1;
- _updateRequest = -1;
- _nTick = _evpUpdateCnt = 0;
- _evpTimer = 0x1f;
- _pReslt = 0x1f;
- _attack = 0;
- _cont = false;
- _evpUpdate = true;
- _timer = 0;
-
- for (int i = 0; i < 3; i++) {
- _channels[i].tick = 0;
- _channels[i].smp = _channels[i].out = 0;
- }
-
- for (int i = 0; i < 14; i++)
- writeReg(i, 0, true);
-
- writeReg(7, 0xbf, true);
-}
-
-void TownsPC98_OpnSquareSineSource::writeReg(uint8 address, uint8 value, bool force) {
- if (!_ready)
- return;
-
- if (address > 10 || *_reg[address] == value) {
- if ((address == 11 || address == 12 || address == 13) && value)
- warning("TownsPC98_OpnSquareSineSource: unsupported reg address: %d", address);
- return;
- }
-
- if (!force) {
- if (_updateRequest >= 63) {
- warning("TownsPC98_OpnSquareSineSource: event buffer overflow");
- _updateRequest = -1;
- }
- _updateRequestBuf[++_updateRequest] = value;
- _updateRequestBuf[++_updateRequest] = address;
- return;
- }
-
- *_reg[address] = value;
-}
-
-void TownsPC98_OpnSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) {
- if (!_ready)
- return;
-
- for (uint32 i = 0; i < bufferSize; i++) {
- _timer += _tickLength;
- while (_timer > 0x5B8D80) {
- _timer -= 0x5B8D80;
-
- if (++_nTick >= (_noiseGenerator & 0x1f)) {
- if ((_rand + 1) & 2)
- _outN ^= 1;
-
- _rand = (((_rand & 1) ^ ((_rand >> 3) & 1)) << 16) | (_rand >> 1);
- _nTick = 0;
- }
-
- for (int ii = 0; ii < 3; ii++) {
- if (++_channels[ii].tick >= (((_channels[ii].frqH & 0x0f) << 8) | _channels[ii].frqL)) {
- _channels[ii].tick = 0;
- _channels[ii].smp ^= 1;
- }
- _channels[ii].out = (_channels[ii].smp | ((_chanEnable >> ii) & 1)) & (_outN | ((_chanEnable >> (ii + 3)) & 1));
- }
-
- if (_evpUpdate) {
- if (++_evpUpdateCnt >= 0) {
- _evpUpdateCnt = 0;
-
- if (--_evpTimer < 0) {
- if (_cont) {
- _evpTimer &= 0x1f;
- } else {
- _evpUpdate = false;
- _evpTimer = 0;
- }
- }
- }
- }
- _pReslt = _evpTimer ^ _attack;
- updatesRegs();
- }
-
- int32 finOut = 0;
- for (int ii = 0; ii < 3; ii++) {
- if ((_channels[ii].vol >> 4) & 1)
- finOut += _tleTable[_channels[ii].out ? _pReslt : 0];
- else
- finOut += _tlTable[_channels[ii].out ? (_channels[ii].vol & 0x0f) : 0];
- }
-
- finOut /= 3;
- buffer[i << 1] += finOut;
- buffer[(i << 1) + 1] += finOut;
- }
-}
-
-void TownsPC98_OpnSquareSineSource::updatesRegs() {
- for (int i = 0; i < _updateRequest;) {
- uint8 b = _updateRequestBuf[i++];
- uint8 a = _updateRequestBuf[i++];
- writeReg(a, b, true);
- }
- _updateRequest = -1;
-}
-
-TownsPC98_OpnPercussionSource::TownsPC98_OpnPercussionSource(const uint32 timerbase) :
- _tickLength(timerbase * 2), _timer(0), _ready(false) {
-
- memset(_rhChan, 0, sizeof(RhtChannel) * 6);
- _reg = new uint8 *[40];
-
- _reg[0] = _reg[1] = _reg[2] = _reg[3] = _reg[4] = _reg[5] = _reg[6] = _reg[7] = _reg[8] = _reg[9] = _reg[10] = _reg[11] = _reg[12] = _reg[13] = _reg[14] = _reg[15] = 0;
- _reg[16] = &_rhChan[0].startPosL;
- _reg[17] = &_rhChan[1].startPosL;
- _reg[18] = &_rhChan[2].startPosL;
- _reg[19] = &_rhChan[3].startPosL;
- _reg[20] = &_rhChan[4].startPosL;
- _reg[21] = &_rhChan[5].startPosL;
- _reg[22] = &_rhChan[0].startPosH;
- _reg[23] = &_rhChan[1].startPosH;
- _reg[24] = &_rhChan[2].startPosH;
- _reg[25] = &_rhChan[3].startPosH;
- _reg[26] = &_rhChan[4].startPosH;
- _reg[27] = &_rhChan[5].startPosH;
- _reg[28] = &_rhChan[0].endPosL;
- _reg[29] = &_rhChan[1].endPosL;
- _reg[30] = &_rhChan[2].endPosL;
- _reg[31] = &_rhChan[3].endPosL;
- _reg[32] = &_rhChan[4].endPosL;
- _reg[33] = &_rhChan[5].endPosL;
- _reg[34] = &_rhChan[0].endPosH;
- _reg[35] = &_rhChan[1].endPosH;
- _reg[36] = &_rhChan[2].endPosH;
- _reg[37] = &_rhChan[3].endPosH;
- _reg[38] = &_rhChan[4].endPosH;
- _reg[39] = &_rhChan[5].endPosH;
-}
-
-void TownsPC98_OpnPercussionSource::init(const uint8 *instrData) {
- if (_ready) {
- reset();
- return;
- }
-
- const uint8 *start = instrData;
- const uint8 *pos = start;
-
- if (instrData) {
- for (int i = 0; i < 6; i++) {
- _rhChan[i].data = start + READ_BE_UINT16(pos);
- pos += 2;
- _rhChan[i].size = READ_BE_UINT16(pos);
- pos += 2;
- }
- reset();
- _ready = true;
- } else {
- memset(_rhChan, 0, sizeof(RhtChannel) * 6);
- _ready = false;
- }
-}
-
-void TownsPC98_OpnPercussionSource::reset() {
- _timer = 0;
- _totalLevel = 63;
-
- for (int i = 0; i < 6; i++) {
- RhtChannel *s = &_rhChan[i];
- s->pos = s->start = s->data;
- s->end = s->data + s->size;
- s->active = false;
- s->level = 0;
- s->out = 0;
- s->decStep = 1;
- s->decState = 0;
- s->samples[0] = s->samples[1] = 0;
- s->startPosH = s->startPosL = s->endPosH = s->endPosL = 0;
- }
-}
-
-void TownsPC98_OpnPercussionSource::writeReg(uint8 address, uint8 value) {
- if (!_ready)
- return;
-
- uint8 h = address >> 4;
- uint8 l = address & 15;
-
- if (address > 15)
- *_reg[address] = value;
-
- if (address == 0) {
- if (value & 0x80) {
- //key off
- for (int i = 0; i < 6; i++) {
- if ((value >> i) & 1)
- _rhChan[i].active = false;
- }
- } else {
- //key on
- for (int i = 0; i < 6; i++) {
- if ((value >> i) & 1) {
- RhtChannel *s = &_rhChan[i];
- s->pos = s->start;
- s->active = true;
- s->out = 0;
- s->samples[0] = s->samples[1] = 0;
- s->decStep = 1;
- s->decState = 0;
- }
- }
- }
- } else if (address == 1) {
- // total level
- _totalLevel = (value & 63) ^ 63;
- for (int i = 0; i < 6; i++)
- recalcOuput(&_rhChan[i]);
- } else if (!h && l & 8) {
- // instrument level
- l &= 7;
- _rhChan[l].level = (value & 0x1f) ^ 0x1f;
- recalcOuput(&_rhChan[l]);
- } else if (h & 3) {
- l &= 7;
- if (h == 1) {
- // set start offset
- _rhChan[l].start = _rhChan[l].data + ((_rhChan[l].startPosH << 8 | _rhChan[l].startPosL) << 8);
- } else if (h == 2) {
- // set end offset
- _rhChan[l].end = _rhChan[l].data + ((_rhChan[l].endPosH << 8 | _rhChan[l].endPosL) << 8) + 255;
- }
- }
-}
-
-void TownsPC98_OpnPercussionSource::nextTick(int32 *buffer, uint32 bufferSize) {
- if (!_ready)
- return;
-
- for (uint32 i = 0; i < bufferSize; i++) {
- _timer += _tickLength;
- while (_timer > 0x5B8D80) {
- _timer -= 0x5B8D80;
-
- for (int ii = 0; ii < 6; ii++) {
- RhtChannel *s = &_rhChan[ii];
- if (s->active) {
- recalcOuput(s);
- if (s->decStep) {
- advanceInput(s);
- if (s->pos == s->end)
- s->active = false;
- }
- s->decStep ^= 1;
- }
- }
- }
-
- int32 finOut = 0;
-
- for (int ii = 0; ii < 6; ii++) {
- if (_rhChan[ii].active)
- finOut += _rhChan[ii].out;
- }
-
- finOut <<= 1;
-
- buffer[i << 1] += finOut;
- buffer[(i << 1) + 1] += finOut;
- }
-}
-
-void TownsPC98_OpnPercussionSource::recalcOuput(RhtChannel *ins) {
- uint32 s = _totalLevel + ins->level;
- uint32 x = s > 62 ? 0 : (1 + (s >> 3));
- int32 y = s > 62 ? 0 : (15 - (s & 7));
- ins->out = ((ins->samples[ins->decStep] * y) >> x) & ~3;
-}
-
-void TownsPC98_OpnPercussionSource::advanceInput(RhtChannel *ins) {
- static const int8 adjustIndex[] = {-1, -1, -1, -1, 2, 5, 7, 9 };
-
- static const int16 stepTable[] = { 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
- };
-
- uint8 cur = (int8) *ins->pos++;
-
- for (int i = 0; i < 2; i++) {
- int b = (2 * (cur & 7) + 1) * stepTable[ins->decState] / 8;
- ins->samples[i] = CLIP<int16>(ins->samples[i ^ 1] + (cur & 8 ? b : -b), -2048, 2047);
- ins->decState = CLIP<int8>(ins->decState + adjustIndex[cur & 7], 0, 48);
- cur >>= 4;
- }
-}
-
-TownsPC98_OpnCore::TownsPC98_OpnCore(Audio::Mixer *mixer, OpnType type) :
- _mixer(mixer),
- _chanInternal(0), _ssg(0), _prc(0),
- _numChan(type == OD_TYPE26 ? 3 : 6), _numSSG(type == OD_TOWNS ? 0 : 3), _hasPercussion(type == OD_TYPE86 ? true : false),
- _oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0),
- _baserate(55125.0f / (float)mixer->getOutputRate()),
- _regProtectionFlag(false), _ready(false) {
-
- memset(&_timers[0], 0, sizeof(OpnTimer));
- memset(&_timers[1], 0, sizeof(OpnTimer));
- _timers[0].cb = &TownsPC98_OpnCore::timerCallbackA;
- _timers[1].cb = &TownsPC98_OpnCore::timerCallbackB;
- _timerbase = (uint32)(_baserate * 1000000.0f);
-}
-
-TownsPC98_OpnCore::~TownsPC98_OpnCore() {
- Common::StackLock lock(_mutex);
- _mixer->stopHandle(_soundHandle);
- delete _ssg;
- delete _prc;
- delete[] _chanInternal;
-
- delete[] _oprRates;
- delete[] _oprRateshift;
- delete[] _oprFrq;
- delete[] _oprAttackDecay;
- delete[] _oprSinTbl;
- delete[] _oprLevelOut;
- delete[] _oprDetune;
-}
-
-bool TownsPC98_OpnCore::init() {
- if (_ready) {
- reset();
- return true;
- }
-
- generateTables();
-
- _chanInternal = new ChanInternal[_numChan];
- for (int i = 0; i < _numChan; i++) {
- memset(&_chanInternal[i], 0, sizeof(ChanInternal));
- for (int j = 0; j < 4; ++j)
- _chanInternal[i].opr[j] = new TownsPC98_OpnOperator(_timerbase, _oprRates, _oprRateshift, _oprAttackDecay, _oprFrq, _oprSinTbl, _oprLevelOut, _oprDetune);
- }
-
- if (_numSSG) {
- _ssg = new TownsPC98_OpnSquareSineSource(_timerbase);
- _ssg->init(&_ssgTables[0], &_ssgTables[16]);
- }
-
- if (_hasPercussion) {
- _prc = new TownsPC98_OpnPercussionSource(_timerbase);
- _prc->init(_percussionData);
- }
-
- _mixer->playStream(Audio::Mixer::kMusicSoundType,
- &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
- _ready = true;
-
- return true;
-}
-
-void TownsPC98_OpnCore::reset() {
- for (int i = 0; i < _numChan; i++) {
- for (int ii = 0; ii < 4; ii++)
- _chanInternal[i].opr[ii]->reset();
- memset(&_chanInternal[i].feedbuf, 0, 3);
- _chanInternal[i].algorithm = 0;
- _chanInternal[i].frqTemp = 0;
- _chanInternal[i].enableLeft = _chanInternal[i].enableRight = true;
- _chanInternal[i].updateEnvelopeParameters = false;
- }
-
- writeReg(0, 0x27, 0x33);
-
- if (_ssg)
- _ssg->reset();
-
- if (_prc)
- _prc->reset();
-}
-
-void TownsPC98_OpnCore::writeReg(uint8 part, uint8 regAddress, uint8 value) {
- if (_regProtectionFlag || !_ready)
- return;
-
- static const uint8 oprOrdr[] = { 0, 2, 1, 3 };
-
- uint8 h = regAddress & 0xf0;
- uint8 l = (regAddress & 0x0f);
-
- ChanInternal *c = 0;
- TownsPC98_OpnOperator **co = 0;
- TownsPC98_OpnOperator *o = 0;
-
- if (regAddress > 0x2F) {
- c = &_chanInternal[(l & 3) + 3 * part];
- co = c->opr;
- o = c->opr[oprOrdr[(l - (l & 3)) >> 2]];
- } else if (regAddress == 0x28) {
- c = &_chanInternal[(value & 3) + ((value & 4) ? 3 : 0)];
- co = c->opr;
- }
-
- switch (h) {
- case 0x00:
- // ssg
- if (_ssg)
- _ssg->writeReg(l, value);
- break;
- case 0x10:
- // pcm rhythm channel
- if (_prc)
- _prc->writeReg(l, value);
- break;
- case 0x20:
- if (l == 8) {
- // Key on/off
- for (int i = 0; i < 4; i++) {
- if ((value >> (4 + i)) & 1)
- co[oprOrdr[i]]->keyOn();
- else
- co[oprOrdr[i]]->keyOff();
- }
- } else if (l == 4) {
- // Timer A
- _timers[0].value = (_timers[0].value & 0xff00) | value;
- } else if (l == 5) {
- // Timer A
- _timers[0].value = (_timers[0].value & 0xff) | (value << 8);
- } else if (l == 6) {
- // Timer B
- _timers[1].value = value & 0xff;
- } else if (l == 7) {
- _timers[0].enabled = (value & 1) ? 1 : 0;
- _timers[1].enabled = (value & 2) ? 1 : 0;
-
- float spc = (float)(0x400 - _timers[0].value) / _baserate;
- _timers[0].smpPerCb = (int32) spc;
- _timers[0].smpPerCbRem = (uint32) ((spc - (float)_timers[0].smpPerCb) * 1000000.0f);
-
- spc = (float)(0x100 - _timers[1].value) * 16.0f / _baserate;
- _timers[1].smpPerCb = (int32) spc;
- _timers[1].smpPerCbRem = (uint32) ((spc - (float)_timers[1].smpPerCb) * 1000000.0f);
-
- if (value & 10) {
- _timers[0].smpTillCb = _timers[0].smpPerCb;
- _timers[0].smpTillCbRem = _timers[0].smpTillCbRem;
- }
-
- if (value & 20) {
- _timers[1].smpTillCb = _timers[1].smpPerCb;
- _timers[1].smpTillCbRem = _timers[1].smpTillCbRem;
- }
- } else if (l == 2) {
- // LFO
- warning("TownsPC98_OpnDriver: TRYING TO USE LFO (NOT SUPPORTED)");
- } else if (l == 10 || l == 11) {
- // DAC
- warning("TownsPC98_OpnDriver: TRYING TO USE DAC (NOT SUPPORTED)");
- }
- break;
-
- case 0x30:
- // detune, multiple
- o->detune((value >> 4) & 7);
- o->multiple(value & 0x0f);
- c->updateEnvelopeParameters = true;
- break;
-
- case 0x40:
- // total level
- o->totalLevel(value & 0x7f);
- break;
-
- case 0x50:
- // rate scaling, attack rate
- o->attackRate(value & 0x1f);
- if (o->scaleRate(value >> 6))
- c->updateEnvelopeParameters = true;
- break;
-
- case 0x60:
- // first decay rate, amplitude modulation
- o->decayRate(value & 0x1f);
- if (value & 0x80)
- warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION (NOT SUPPORTED)");
- break;
-
- case 0x70:
- // secondary decay rate
- o->sustainRate(value & 0x1f);
- break;
-
- case 0x80:
- // secondary amplitude, release rate;
- o->sustainLevel(value >> 4);
- o->releaseRate(value & 0x0f);
- break;
-
- case 0x90:
- warning("TownsPC98_OpnDriver: TRYING TO SSG ENVELOPE SHAPES (NOT SUPPORTED)");
- break;
-
- case 0xa0:
- // frequency
- l &= ~3;
- if (l == 0) {
- c->frqTemp = (c->frqTemp & 0xff00) | value;
- c->updateEnvelopeParameters = true;
- for (int i = 0; i < 4; i++)
- co[i]->frequency(c->frqTemp);
- } else if (l == 4) {
- c->frqTemp = (c->frqTemp & 0xff) | (value << 8);
- } else if (l == 8) {
- // Ch 3/6 special mode frq
- warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
- } else if (l == 12) {
- // Ch 3/6 special mode frq
- warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
- }
- break;
-
- case 0xb0:
- l &= ~3;
- if (l == 0) {
- // feedback, _algorithm
- co[0]->feedbackLevel((value >> 3) & 7);
- c->algorithm = value & 7;
- } else if (l == 4) {
- // stereo, LFO sensitivity
- c->enableLeft = value & 0x80 ? true : false;
- c->enableRight = value & 0x40 ? true : false;
- uint8 ams = (value & 0x3F) >> 3;
- if (ams)
- warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION SENSITIVITY (NOT SUPPORTED)");
- uint8 fms = value & 3;
- if (fms)
- warning("TownsPC98_OpnDriver: TRYING TO USE FREQ MODULATION SENSITIVITY (NOT SUPPORTED)");
- }
- break;
-
- default:
- warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAddress);
- }
-}
-
-int TownsPC98_OpnCore::readBuffer(int16 *buffer, const int numSamples) {
- Common::StackLock lock(_mutex);
-
- memset(buffer, 0, sizeof(int16) * numSamples);
- int32 *tmp = new int32[numSamples];
- int32 *tmpStart = tmp;
- memset(tmp, 0, sizeof(int32) * numSamples);
- int32 samplesLeft = numSamples >> 1;
-
- while (samplesLeft) {
- int32 render = samplesLeft;
-
- for (int i = 0; i < 2; i++) {
- if (_timers[i].enabled && _timers[i].cb) {
- if (!_timers[i].smpTillCb) {
- (this->*_timers[i].cb)();
- _timers[i].smpTillCb = _timers[i].smpPerCb;
-
- _timers[i].smpTillCbRem += _timers[i].smpPerCbRem;
- if (_timers[i].smpTillCbRem >= _timerbase) {
- _timers[i].smpTillCb++;
- _timers[i].smpTillCbRem -= _timerbase;
- }
- }
- render = MIN(render, _timers[i].smpTillCb);
- }
- }
-
- samplesLeft -= render;
-
- for (int i = 0; i < 2; i++) {
- if (_timers[i].enabled && _timers[i].cb) {
- _timers[i].smpTillCb -= render;
- }
- }
-
- nextTick(tmp, render);
-
- if (_ssg)
- _ssg->nextTick(tmp, render);
- if (_prc)
- _prc->nextTick(tmp, render);
-
- for (int i = 0; i < render; ++i) {
- int32 l = CLIP<int32>(tmp[i << 1], -32767, 32767);
- buffer[i << 1] = (int16) l;
- int32 r = CLIP<int32>(tmp[(i << 1) + 1], -32767, 32767);
- buffer[(i << 1) + 1] = (int16) r;
- }
-
- buffer += (render << 1);
- tmp += (render << 1);
- }
-
- delete[] tmpStart;
- return numSamples;
-}
-
-void TownsPC98_OpnCore::generateTables() {
- delete[] _oprRates;
- _oprRates = new uint8[128];
-
- WRITE_BE_UINT32(_oprRates + 32, _numChan == 6 ? 0x90900000 : 0x00081018);
- WRITE_BE_UINT32(_oprRates + 36, _numChan == 6 ? 0x00001010 : 0x00081018);
- memset(_oprRates, 0x90, 32);
- memset(_oprRates + 96, 0x80, 32);
- uint8 *dst = (uint8 *)_oprRates + 40;
- for (int i = 0; i < 40; i += 4)
- WRITE_BE_UINT32(dst + i, 0x00081018);
- for (int i = 0; i < 48; i += 4)
- WRITE_BE_UINT32(dst + i, 0x00081018);
- dst += 40;
- for (uint8 i = 0; i < 16; i ++) {
- uint8 v = (i < 12) ? i : 12;
- *dst++ = ((4 + v) << 3);
- }
-
- delete[] _oprRateshift;
- _oprRateshift = new uint8[128];
- memset(_oprRateshift, 0, 128);
- dst = (uint8 *)_oprRateshift + 32;
- for (int i = 11; i; i--) {
- memset(dst, i, 4);
- dst += 4;
- }
-
- delete[] _oprFrq;
- _oprFrq = new uint32[0x1000];
- for (uint32 i = 0; i < 0x1000; i++)
- _oprFrq[i] = (uint32)(_baserate * (float)(i << 11));
-
- delete[] _oprAttackDecay;
- _oprAttackDecay = new uint8[152];
- memset(_oprAttackDecay, 0, 152);
- for (int i = 0; i < 36; i++)
- WRITE_BE_UINT32(_oprAttackDecay + (i << 2), _adtStat[i]);
-
- delete[] _oprSinTbl;
- _oprSinTbl = new uint32[1024];
- for (int i = 0; i < 1024; i++) {
- double val = sin((double) (((i << 1) + 1) * PI / 1024.0));
- double d_dcb = log(1.0 / (double)ABS(val)) / log(2.0) * 256.0;
- int32 i_dcb = (int32)(2.0 * d_dcb);
- i_dcb = (i_dcb & 1) ? (i_dcb >> 1) + 1 : (i_dcb >> 1);
- _oprSinTbl[i] = (i_dcb << 1) + (val >= 0.0 ? 0 : 1);
- }
-
- delete[] _oprLevelOut;
- _oprLevelOut = new int32[0x1a00];
- for (int i = 0; i < 256; i++) {
- double val = floor(65536.0 / pow(2.0, 0.00390625 * (double)(1 + i)));
- int32 val_int = ((int32) val) >> 4;
- _oprLevelOut[i << 1] = (val_int & 1) ? ((val_int >> 1) + 1) << 2 : (val_int >> 1) << 2;
- _oprLevelOut[(i << 1) + 1] = -_oprLevelOut[i << 1];
- for (int ii = 1; ii < 13; ii++) {
- _oprLevelOut[(i << 1) + (ii << 9)] = _oprLevelOut[i << 1] >> ii;
- _oprLevelOut[(i << 1) + (ii << 9) + 1] = -_oprLevelOut[(i << 1) + (ii << 9)];
- }
- }
-
- uint8 *dtt = new uint8[128];
- memset(dtt, 0, 36);
- memset(dtt + 36, 1, 8);
- memcpy(dtt + 44, _detSrc, 84);
-
- delete[] _oprDetune;
- _oprDetune = new int32[256];
- for (int i = 0; i < 128; i++) {
- _oprDetune[i] = (int32) ((float)dtt[i] * _baserate * 64.0);
- _oprDetune[i + 128] = -_oprDetune[i];
- }
-
- delete[] dtt;
-}
-
-void TownsPC98_OpnCore::nextTick(int32 *buffer, uint32 bufferSize) {
- if (!_ready)
- return;
-
- for (int i = 0; i < _numChan; i++) {
- TownsPC98_OpnOperator **o = _chanInternal[i].opr;
-
- if (_chanInternal[i].updateEnvelopeParameters) {
- _chanInternal[i].updateEnvelopeParameters = false;
- for (int ii = 0; ii < 4 ; ii++)
- o[ii]->updatePhaseIncrement();
- }
-
- for (uint32 ii = 0; ii < bufferSize ; ii++) {
- int32 phbuf1, phbuf2, output;
- phbuf1 = phbuf2 = output = 0;
-
- int32 *leftSample = &buffer[ii * 2];
- int32 *rightSample = &buffer[ii * 2 + 1];
- int32 *del = &_chanInternal[i].feedbuf[2];
- int32 *feed = _chanInternal[i].feedbuf;
-
- switch (_chanInternal[i].algorithm) {
- case 0:
- o[0]->generateOutput(0, feed, phbuf1);
- o[2]->generateOutput(*del, 0, phbuf2);
- *del = 0;
- o[1]->generateOutput(phbuf1, 0, *del);
- o[3]->generateOutput(phbuf2, 0, output);
- break;
- case 1:
- o[0]->generateOutput(0, feed, phbuf1);
- o[2]->generateOutput(*del, 0, phbuf2);
- o[1]->generateOutput(0, 0, phbuf1);
- o[3]->generateOutput(phbuf2, 0, output);
- *del = phbuf1;
- break;
- case 2:
- o[0]->generateOutput(0, feed, phbuf2);
- o[2]->generateOutput(*del, 0, phbuf2);
- o[1]->generateOutput(0, 0, phbuf1);
- o[3]->generateOutput(phbuf2, 0, output);
- *del = phbuf1;
- break;
- case 3:
- o[0]->generateOutput(0, feed, phbuf2);
- o[2]->generateOutput(0, 0, *del);
- o[1]->generateOutput(phbuf2, 0, phbuf1);
- o[3]->generateOutput(*del, 0, output);
- *del = phbuf1;
- break;
- case 4:
- o[0]->generateOutput(0, feed, phbuf1);
- o[2]->generateOutput(0, 0, phbuf2);
- o[1]->generateOutput(phbuf1, 0, output);
- o[3]->generateOutput(phbuf2, 0, output);
- *del = 0;
- break;
- case 5:
- o[0]->generateOutput(0, feed, phbuf1);
- o[2]->generateOutput(*del, 0, output);
- o[1]->generateOutput(phbuf1, 0, output);
- o[3]->generateOutput(phbuf1, 0, output);
- *del = phbuf1;
- break;
- case 6:
- o[0]->generateOutput(0, feed, phbuf1);
- o[2]->generateOutput(0, 0, output);
- o[1]->generateOutput(phbuf1, 0, output);
- o[3]->generateOutput(0, 0, output);
- *del = 0;
- break;
- case 7:
- o[0]->generateOutput(0, feed, output);
- o[2]->generateOutput(0, 0, output);
- o[1]->generateOutput(0, 0, output);
- o[3]->generateOutput(0, 0, output);
- *del = 0;
- break;
- };
-
- int32 finOut = (output << 2) / ((_numChan + _numSSG - 3) / 3);
-
- if (_chanInternal[i].enableLeft)
- *leftSample += finOut;
-
- if (_chanInternal[i].enableRight)
- *rightSample += finOut;
- }
- }
-}
-
-TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) : TownsPC98_OpnCore(mixer, type),
- _channels(0), _ssgChannels(0), _sfxChannels(0), _rhythmChannel(0),
- _trackPtr(0), _sfxData(0), _sfxOffs(0), _ssgPatches(0),
- _patches(0), _sfxBuffer(0), _musicBuffer(0),
-
- _opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 108), _opnFreqTableSSG(_drvTables + 132),
- _opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 84)),
-
- _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), _finishedChannelsFlag(0),
- _updateSSGFlag(type == OD_TOWNS ? 0x00 : 0x07), _finishedSSGFlag(0),
- _updateRhythmFlag(type == OD_TYPE86 ? 0x01 : 0x00), _finishedRhythmFlag(0),
- _updateSfxFlag(type == OD_TOWNS ? 0x00 : 0x06), _finishedSfxFlag(0),
-
- _musicTickCounter(0),
-
- _musicPlaying(false), _sfxPlaying(false), _fading(false), _looping(0), _ready(false) {
- _sfxOffsets[0] = _sfxOffsets[1] = 0;
-}
-
-TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
- reset();
-
- if (_channels) {
- for (int i = 0; i < _numChan; i++)
- delete _channels[i];
- delete[] _channels;
- }
-
- if (_ssgChannels) {
- for (int i = 0; i < _numSSG; i++)
- delete _ssgChannels[i];
- delete[] _ssgChannels;
- }
-
- if (_sfxChannels) {
- for (int i = 0; i < 2; i++)
- delete _sfxChannels[i];
- delete[] _sfxChannels;
- }
-
- delete _rhythmChannel;
-
- delete[] _ssgPatches;
-}
-
-bool TownsPC98_OpnDriver::init() {
- if (_ready) {
- reset();
- return true;
- }
-
- TownsPC98_OpnCore::init();
-
- _channels = new TownsPC98_OpnChannel *[_numChan];
- for (int i = 0; i < _numChan; i++) {
- int ii = i * 6;
- _channels[i] = new TownsPC98_OpnChannel(this, _drvTables[ii], _drvTables[ii + 1],
- _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]);
- _channels[i]->init();
- }
-
- if (_numSSG) {
- _ssgPatches = new uint8[256];
- memcpy(_ssgPatches, _drvTables + 156, 256);
-
- _ssgChannels = new TownsPC98_OpnChannelSSG *[_numSSG];
- for (int i = 0; i < _numSSG; i++) {
- int ii = i * 6;
- _ssgChannels[i] = new TownsPC98_OpnChannelSSG(this, _drvTables[ii], _drvTables[ii + 1],
- _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]);
- _ssgChannels[i]->init();
- }
-
- _sfxChannels = new TownsPC98_OpnSfxChannel *[2];
- for (int i = 0; i < 2; i++) {
- int ii = (i + 1) * 6;
- _sfxChannels[i] = new TownsPC98_OpnSfxChannel(this, _drvTables[ii], _drvTables[ii + 1],
- _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]);
- _sfxChannels[i]->init();
- }
- }
-
- if (_hasPercussion) {
- _rhythmChannel = new TownsPC98_OpnChannelPCM(this, 0, 0, 0, 0, 0, 1);
- _rhythmChannel->init();
- }
-
- setMusicTempo(84);
- setSfxTempo(654);
-
- _ready = true;
-
- return true;
-}
-
-void TownsPC98_OpnDriver::loadMusicData(uint8 *data, bool loadPaused) {
- if (!_ready) {
- warning("TownsPC98_OpnDriver: Driver must be initialized before loading data");
- return;
- }
-
- if (!data) {
- warning("TownsPC98_OpnDriver: Invalid music file data");
- return;
- }
-
- reset();
-
- Common::StackLock lock(_mutex);
- uint8 *src_a = _trackPtr = _musicBuffer = data;
-
- for (uint8 i = 0; i < 3; i++) {
- _channels[i]->loadData(data + READ_LE_UINT16(src_a));
- src_a += 2;
- }
-
- for (int i = 0; i < _numSSG; i++) {
- _ssgChannels[i]->loadData(data + READ_LE_UINT16(src_a));
- src_a += 2;
- }
-
- for (uint8 i = 3; i < _numChan; i++) {
- _channels[i]->loadData(data + READ_LE_UINT16(src_a));
- src_a += 2;
- }
-
- if (_hasPercussion) {
- _rhythmChannel->loadData(data + READ_LE_UINT16(src_a));
- src_a += 2;
- }
-
- toggleRegProtection(false);
-
- _patches = src_a + 4;
- _finishedChannelsFlag = _finishedSSGFlag = _finishedRhythmFlag = 0;
-
- _musicPlaying = (loadPaused ? false : true);
-}
-
-void TownsPC98_OpnDriver::loadSoundEffectData(uint8 *data, uint8 trackNum) {
- if (!_ready) {
- warning("TownsPC98_OpnDriver: Driver must be initialized before loading data");
- return;
- }
-
- if (!_sfxChannels) {
- warning("TownsPC98_OpnDriver: Sound effects not supported by this configuration");
- return;
- }
-
- if (!data) {
- warning("TownsPC98_OpnDriver: Invalid sound effects file data");
- return;
- }
-
- Common::StackLock lock(_mutex);
- _sfxData = _sfxBuffer = data;
- _sfxOffsets[0] = READ_LE_UINT16(&_sfxData[(trackNum << 2)]);
- _sfxOffsets[1] = READ_LE_UINT16(&_sfxData[(trackNum << 2) + 2]);
- _sfxPlaying = true;
- _finishedSfxFlag = 0;
-}
-
-void TownsPC98_OpnDriver::reset() {
- Common::StackLock lock(_mutex);
-
- _musicPlaying = false;
- _sfxPlaying = false;
- _fading = false;
- _looping = 0;
- _musicTickCounter = 0;
- _sfxData = 0;
-
- TownsPC98_OpnCore::reset();
-
- for (int i = 0; i < _numChan; i++)
- _channels[i]->reset();
- for (int i = 0; i < _numSSG; i++)
- _ssgChannels[i]->reset();
-
- if (_numSSG) {
- for (int i = 0; i < 2; i++)
- _sfxChannels[i]->reset();
-
- memcpy(_ssgPatches, _drvTables + 156, 256);
- }
-
- if (_rhythmChannel)
- _rhythmChannel->reset();
-}
-
-void TownsPC98_OpnDriver::fadeStep() {
- if (!_musicPlaying)
- return;
-
- Common::StackLock lock(_mutex);
- for (int j = 0; j < _numChan; j++) {
- if (_updateChannelsFlag & _channels[j]->_idFlag)
- _channels[j]->fadeStep();
- }
-
- for (int j = 0; j < _numSSG; j++) {
- if (_updateSSGFlag & _ssgChannels[j]->_idFlag)
- _ssgChannels[j]->fadeStep();
- }
-
- if (!_fading) {
- _fading = 19;
- if (_hasPercussion) {
- if (_updateRhythmFlag & _rhythmChannel->_idFlag)
- _rhythmChannel->reset();
- }
- } else {
- if (!--_fading)
- reset();
- }
-}
-
-void TownsPC98_OpnDriver::timerCallbackB() {
- _sfxOffs = 0;
-
- if (_musicPlaying) {
- _musicTickCounter++;
-
- for (int i = 0; i < _numChan; i++) {
- if (_updateChannelsFlag & _channels[i]->_idFlag) {
- _channels[i]->processEvents();
- _channels[i]->processFrequency();
- }
- }
-
- for (int i = 0; i < _numSSG; i++) {
- if (_updateSSGFlag & _ssgChannels[i]->_idFlag) {
- _ssgChannels[i]->processEvents();
- _ssgChannels[i]->processFrequency();
- }
- }
-
- if (_hasPercussion)
- if (_updateRhythmFlag & _rhythmChannel->_idFlag)
- _rhythmChannel->processEvents();
- }
-
- toggleRegProtection(false);
-
- if (_finishedChannelsFlag == _updateChannelsFlag && _finishedSSGFlag == _updateSSGFlag && _finishedRhythmFlag == _updateRhythmFlag)
- _musicPlaying = false;
-}
-
-void TownsPC98_OpnDriver::timerCallbackA() {
- if (_sfxChannels && _sfxPlaying) {
- if (_sfxData)
- startSoundEffect();
-
- _sfxOffs = 3;
- _trackPtr = _sfxBuffer;
-
- for (int i = 0; i < 2; i++) {
- if (_updateSfxFlag & _sfxChannels[i]->_idFlag) {
- _sfxChannels[i]->processEvents();
- _sfxChannels[i]->processFrequency();
- }
- }
-
- _trackPtr = _musicBuffer;
- }
-
- if (_finishedSfxFlag == _updateSfxFlag)
- _sfxPlaying = false;
-}
-
-void TownsPC98_OpnDriver::setMusicTempo(uint8 tempo) {
- writeReg(0, 0x26, tempo);
- writeReg(0, 0x27, 0x33);
-}
-
-void TownsPC98_OpnDriver::setSfxTempo(uint16 tempo) {
- writeReg(0, 0x24, tempo & 0xff);
- writeReg(0, 0x25, tempo >> 8);
- writeReg(0, 0x27, 0x33);
-}
-
-void TownsPC98_OpnDriver::startSoundEffect() {
- for (int i = 0; i < 2; i++) {
- if (_sfxOffsets[i]) {
- _ssgChannels[i + 1]->protect();
- _sfxChannels[i]->reset();
- _sfxChannels[i]->loadData(_sfxData + _sfxOffsets[i]);
- }
- }
-
- _sfxData = 0;
-}
-
-const uint8 TownsPC98_OpnDriver::_drvTables[] = {
- // channel presets
- 0x00, 0x80, 0x00, 0x00, 0x00, 0x01,
- 0x01, 0x80, 0x01, 0x01, 0x00, 0x02,
- 0x02, 0x80, 0x02, 0x02, 0x00, 0x04,
- 0x00, 0x80, 0x03, 0x04, 0x01, 0x08,
- 0x01, 0x80, 0x04, 0x05, 0x01, 0x10,
- 0x02, 0x80, 0x05, 0x06, 0x01, 0x20,
-
- // control event size
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05,
- 0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02,
-
- // fmt level presets
- 0x54, 0x50, 0x4C, 0x48, 0x44, 0x40, 0x3C, 0x38,
- 0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18,
- 0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90,
-
- // carriers
- 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F,
-
- // pc98 level presets
- 0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25,
- 0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10,
- 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90,
-
- // frequencies
- 0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02, 0xDF, 0x02,
- 0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03,
- 0xD5, 0x03, 0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04,
-
- // ssg frequencies
- 0xE8, 0x0E, 0x12, 0x0E, 0x48, 0x0D, 0x89, 0x0C,
- 0xD5, 0x0B, 0x2B, 0x0B, 0x8A, 0x0A, 0xF3, 0x09,
- 0x64, 0x09, 0xDD, 0x08, 0x5E, 0x08, 0xE6, 0x07,
-
- // ssg patch data
- 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
- 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00,
- 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0x37, 0x81, 0xC8, 0x00,
- 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
- 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
- 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
- 0x04, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xBE, 0x00,
- 0x0A, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0x01, 0x00,
- 0xFF, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
- 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x00,
- 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x64, 0x01, 0xFF, 0x64, 0xFF, 0x81, 0xFF, 0x00,
- 0x01, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
-
- 0x02, 0x01, 0xFF, 0x28, 0xFF, 0x81, 0xF0, 0x00,
- 0x00, 0x81, 0x00, 0x00, 0x0A, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xC8, 0x00,
- 0x01, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0x78, 0x5F, 0x81, 0xA0, 0x00,
- 0x05, 0x81, 0x00, 0x00, 0x28, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
- 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
- 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00,
- 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x81, 0x00, 0x00,
- 0x00, 0x81, 0x00, 0x00, 0xFF, 0x81, 0x00, 0x00
-};
-
SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
- : Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _sfxFileData(0),
- _sfxFileIndex((uint)-1), _sfxWDTable(0), _sfxBTTable(0), _parser(0) {
+ : Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _musicTrackData(0), _sfxFileData(0), _cdaPlaying(0),
+ _sfxFileIndex((uint)-1), _musicFadeTable(0), _sfxWDTable(0), _sfxBTTable(0), _sfxChannel(0x46) {
- _driver = new Towns_EuphonyDriver(_mixer);
- int ret = open();
- if (ret != MERR_ALREADY_OPEN && ret != 0)
- error("couldn't open midi driver");
+ _driver = new TownsEuphonyDriver(_mixer);
}
SoundTowns::~SoundTowns() {
AudioCD.stop();
haltTrack();
+ delete[] _musicTrackData;
delete[] _sfxFileData;
-
- Common::StackLock lock(_mutex);
- _driver->setTimerCallback(0, 0);
- close();
-
- _driver = 0;
}
bool SoundTowns::init() {
_vm->checkCD();
int unused = 0;
+ _musicFadeTable = _vm->staticres()->loadRawData(k1TownsMusicFadeTable, unused);
_sfxWDTable = _vm->staticres()->loadRawData(k1TownsSFXwdTable, unused);
_sfxBTTable = _vm->staticres()->loadRawData(k1TownsSFXbtTable, unused);
+ _musicTrackData = new uint8[50570];
+
+ if (!_driver->init())
+ return false;
- return loadInstruments();
+ if (!loadInstruments())
+ return false;
+
+ _driver->intf()->callback(68);
+ _driver->intf()->callback(70, 0x33);
+ _driver->setOutputVolume(1, 118, 118);
+
+ return true;
}
void SoundTowns::process() {
@@ -3816,10 +94,13 @@ void SoundTowns::playTrack(uint8 track) {
beginFadeOut();
if (_musicEnabled == 2 && trackNum != -1) {
+ _driver->setOutputVolume(1, 118, 118);
AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0);
AudioCD.updateCD();
+ _cdaPlaying = true;
} else if (_musicEnabled) {
playEuphonyTrack(READ_LE_UINT32(&tTable[tTableIndex]), loop);
+ _cdaPlaying = false;
}
_lastTrack = track;
@@ -3829,15 +110,15 @@ void SoundTowns::haltTrack() {
_lastTrack = -1;
AudioCD.stop();
AudioCD.updateCD();
- if (_parser) {
- Common::StackLock lock(_mutex);
- _parser->setTrack(0);
- _parser->jumpToTick(0);
- _parser->unloadMusic();
- delete _parser;
- _parser = 0;
- }
- _driver->queue()->release();
+ _cdaPlaying = false;
+
+ for (int i = 0; i < 6; i++)
+ _driver->chanVolume(i, 0);
+ for (int i = 0x40; i < 0x46; i++)
+ _driver->chanVolume(i, 0);
+ for (int i = 0; i < 32; i++)
+ _driver->chanEnable(i, 0);
+ _driver->stopParser();
}
void SoundTowns::loadSoundFile(uint file) {
@@ -3851,27 +132,26 @@ void SoundTowns::loadSoundFile(uint file) {
void SoundTowns::playSoundEffect(uint8 track) {
if (!_sfxEnabled || !_sfxFileData)
return;
-
+
if (track == 0 || track == 10) {
- _mixer->stopHandle(_sfxHandle);
+ stopAllSoundEffects();
return;
} else if (track == 1) {
- // sfx fadeout
- _mixer->stopHandle(_sfxHandle);
+ fadeOutSoundEffects();
return;
}
- uint8 note = 0x3c;
+ uint8 note = 60;
if (_sfxFileIndex == 5) {
- if (track == 0x10) {
- note = 0x3e;
- track = 0x0f;
- } else if (track == 0x11) {
- note = 0x40;
- track = 0x0f;
- } else if (track == 0x12) {
- note = 0x41;
- track = 0x0f;
+ if (track == 16) {
+ note = 62;
+ track = 15;
+ } else if (track == 17) {
+ note = 64;
+ track = 15;
+ } else if (track == 18) {
+ note = 65;
+ track = 15;
}
}
@@ -3880,32 +160,37 @@ void SoundTowns::playSoundEffect(uint8 track) {
if (offset == -1)
return;
- uint32 *sfxHeader = (uint32 *)(fileBody + offset);
+ if (!_driver->soundEffectIsPlaying(_sfxChannel ^ 1)) {
+ _sfxChannel ^= 1;
+ } else if (_driver->soundEffectIsPlaying(_sfxChannel)) {
+ _sfxChannel ^= 1;
+ _driver->stopSoundEffect(_sfxChannel);
+ }
+ uint32 *sfxHeader = (uint32 *)(fileBody + offset);
uint32 sfxHeaderID = READ_LE_UINT32(sfxHeader);
- uint32 sfxHeaderInBufferSize = READ_LE_UINT32(&sfxHeader[1]);
- uint32 sfxHeaderOutBufferSize = READ_LE_UINT32(&sfxHeader[3]);
- uint32 sfxRootNoteOffs = READ_LE_UINT32(&sfxHeader[7]);
- uint32 sfxRate = READ_LE_UINT32(&sfxHeader[6]);
+ uint32 playbackBufferSize = sfxHeaderID == 1 ? 30704 : READ_LE_UINT32(&sfxHeader[3]);
- uint32 playbackBufferSize = (sfxHeaderID == 1) ? sfxHeaderInBufferSize : sfxHeaderOutBufferSize;
+ uint8 *sfxPlaybackBuffer = new uint8[playbackBufferSize + 32];
+ memcpy(sfxPlaybackBuffer, fileBody + offset, 32);
- uint8 *sfxPlaybackBuffer = (uint8 *)malloc(playbackBufferSize);
- memset(sfxPlaybackBuffer, 0x80, playbackBufferSize);
+ uint8 *dst = sfxPlaybackBuffer + 32;
+ memset(dst, 0x80, playbackBufferSize);
uint8 *sfxBody = ((uint8 *)sfxHeader) + 0x20;
if (!sfxHeaderID) {
- memcpy(sfxPlaybackBuffer, sfxBody, playbackBufferSize);
+ memcpy(dst, sfxBody, playbackBufferSize);
} else if (sfxHeaderID == 1) {
- Screen::decodeFrame4(sfxBody, sfxPlaybackBuffer, playbackBufferSize);
+ Screen::decodeFrame4(sfxBody, dst, playbackBufferSize);
} else if (_sfxWDTable) {
- uint8 *tgt = sfxPlaybackBuffer;
+ uint8 *tgt = dst;
uint32 sfx_BtTable_Offset = 0;
uint32 sfx_WdTable_Offset = 0;
uint32 sfx_WdTable_Number = 5;
+ uint32 inSize = READ_LE_UINT32(&sfxHeader[1]);
- for (uint32 i = 0; i < sfxHeaderInBufferSize; i++) {
+ for (uint32 i = 0; i < inSize; i++) {
sfx_WdTable_Offset = (sfx_WdTable_Number * 3 << 9) + sfxBody[i] * 6;
sfx_WdTable_Number = READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset);
@@ -3917,124 +202,174 @@ void SoundTowns::playSoundEffect(uint8 track) {
}
}
- for (uint32 i = 0; i < playbackBufferSize; i++) {
- if (sfxPlaybackBuffer[i] < 0x80)
- sfxPlaybackBuffer[i] = 0x80 - sfxPlaybackBuffer[i];
- }
+ _driver->chanVolume(_sfxChannel, 127);
+ _driver->chanPanPos(_sfxChannel, 0x40);
+ _driver->chanPitch(_sfxChannel, 0);
+ _driver->playSoundEffect(_sfxChannel, note, 127, sfxPlaybackBuffer);
+}
- playbackBufferSize -= 0x20;
+void SoundTowns::updateVolumeSettings() {
+ if (!_driver)
+ return;
- uint32 outputRate = uint32(11025 * calculatePhaseStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000));
+ bool mute = false;
+ _driver->setSoundEffectVolume(ConfMan.getInt("sfx_volume"));
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
- _currentSFX = Audio::makeRawStream(sfxPlaybackBuffer, playbackBufferSize,
- outputRate, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
- _mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, _currentSFX);
+ _driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+ _driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
+
+void SoundTowns::stopAllSoundEffects() {
+ _driver->chanVolume(0x46, 0);
+ _driver->chanVolume(0x47, 0);
+ _driver->stopSoundEffect(0x46);
+ _driver->stopSoundEffect(0x47);
+ _sfxChannel = 0x46;
}
void SoundTowns::beginFadeOut() {
- _lastTrack = -1;
- _driver->fading();
+ if (_cdaPlaying) {
+ for (int i = 118; i > 103; i--) {
+ _driver->setOutputVolume(1, i, i);
+ _vm->delay(2 * _vm->tickLength());
+ }
- // TODO: this should fade out too
- AudioCD.stop();
- AudioCD.updateCD();
-}
+ for (int i = 103; i > 83; i -= 2) {
+ _driver->setOutputVolume(1, i, i);
+ _vm->delay(2 * _vm->tickLength());
+ }
-int SoundTowns::open() {
- if (!_driver)
- return 255;
+ for (int i = 83; i > 58; i -= 2) {
+ _driver->setOutputVolume(1, i, i);
+ _vm->delay(_vm->tickLength());
+ }
- int ret = _driver->open();
- if (ret)
- return ret;
+ for (int i = 58; i > 0; i--)
+ _driver->setOutputVolume(1, i, i);
- _driver->setTimerCallback(this, &onTimer);
- return 0;
-}
+ _driver->setOutputVolume(1, 0, 0);
-void SoundTowns::close() {
- if (_driver)
- _driver->close();
-}
+ } else {
+ if (_lastTrack == -1)
+ return;
-void SoundTowns::send(uint32 b) {
- _driver->send(b);
-}
+ uint32 ticks = 2;
+ int tickAdv = 0;
+
+ uint16 fadeVolCur[12];
+ uint16 fadeVolStep[12];
+
+ for (int i = 0; i < 6; i++) {
+ fadeVolCur[i] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + i) * 2]);
+ fadeVolStep[i] = fadeVolCur[i] / 50;
+ fadeVolCur[i + 6] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + 6 + i) * 2]);
+ fadeVolStep[i + 6] = fadeVolCur[i + 6] / 30;
+ }
+
+ for (int i = 0; i < 12; i++) {
+ for (int ii = 0; ii < 6; ii++)
+ _driver->chanVolume(ii, fadeVolCur[ii]);
+ for (int ii = 0x40; ii < 0x46; ii++)
+ _driver->chanVolume(ii, fadeVolCur[ii - 0x3a]);
+
+ for (int ii = 0; ii < 6; ii++) {
+ fadeVolCur[ii] -= fadeVolStep[ii];
+ if (fadeVolCur[ii] < 10)
+ fadeVolCur[ii] = 0;
+ fadeVolCur[ii + 6] -= fadeVolStep[ii + 6];
+ if (fadeVolCur[ii + 6] < 10)
+ fadeVolCur[ii + 6] = 0;
+ }
+
+ if (++tickAdv == 3) {
+ tickAdv = 0;
+ ticks += 2;
+ }
+ _vm->delay(ticks * _vm->tickLength());
+ }
+ }
-uint32 SoundTowns::getBaseTempo() {
- return _driver ? _driver->getBaseTempo() : 0;
+ haltTrack();
}
bool SoundTowns::loadInstruments() {
uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
if (!twm)
return false;
- _driver->queue()->loadDataToCurrentPosition(twm, 0x8BF0);
- _driver->loadFmInstruments(_driver->queue()->trackData() + 8);
- _driver->queue()->loadDataToCurrentPosition(twm + 0x0CA0, 0xC58A);
- _driver->loadWaveInstruments(_driver->queue()->trackData() + 8);
+ Common::StackLock lock(_mutex);
+
+ Screen::decodeFrame4(twm, _musicTrackData, 50570);
+ for (int i = 0; i < 128; i++)
+ _driver->loadInstrument(0, i, &_musicTrackData[i * 48 + 8]);
+
+ Screen::decodeFrame4(twm + 3232, _musicTrackData, 50570);
+ for (int i = 0; i < 32; i++)
+ _driver->loadInstrument(0x40, i, &_musicTrackData[i * 128 + 8]);
+
+ _driver->unloadWaveTable(-1);
+ uint8 *src = &_musicTrackData[32 * 128 + 8];
+ for (int i = 0; i < 10; i++) {
+ _driver->loadWaveTable(src);
+ src = src + READ_LE_UINT16(&src[12]) + 32;
+ }
+
+ _driver->reserveSoundEffectChannels(2);
+
delete[] twm;
- _driver->queue()->release();
return true;
}
void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
- uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
Common::StackLock lock(_mutex);
- if (!_parser) {
- _parser = new Towns_EuphonyParser(_driver->queue());
- _parser->setMidiDriver(this);
- _parser->setTimerRate(getBaseTempo());
- }
+ uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0);
+ Screen::decodeFrame4(twm + 19312 + offset, _musicTrackData, 50570);
+ delete[] twm;
+
+ const uint8 *src = _musicTrackData + 852;
+ for (int i = 0; i < 32; i++)
+ _driver->chanEnable(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanMode(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanOrdr(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanVolumeShift(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanNoteShift(i, *src++);
+
+ src = _musicTrackData + 1748;
+ for (int i = 0; i < 6; i++)
+ _driver->assignChannel(i, *src++);
+ for (int i = 0x40; i < 0x46; i++)
+ _driver->assignChannel(i, *src++);
- _parser->property(MidiParser::mpAutoLoop, loop);
- _parser->loadMusic(twm + 0x4b70 + offset, 0xC58A);
+ uint32 trackSize = READ_LE_UINT32(_musicTrackData + 2048);
+ uint8 startTick = _musicTrackData[2052];
+
+ _driver->setMusicTempo(_musicTrackData[2053]);
- delete[] twm;
-}
+ src = _musicTrackData + 2054;
+ uint32 l = READ_LE_UINT32(src + trackSize);
+ trackSize += (l + 4);
+ l = READ_LE_UINT32(src + trackSize);
+ trackSize += (l + 4);
-void SoundTowns::onTimer(void *data) {
- SoundTowns *music = (SoundTowns *)data;
- Common::StackLock lock(music->_mutex);
- if (music->_parser)
- music->_parser->onTimer();
+ _driver->setMusicLoop(loop);
+ _driver->startMusicTrack(src, trackSize, startTick);
}
-float SoundTowns::calculatePhaseStep(int8 semiTone, int8 semiToneRootkey,
- uint32 sampleRate, uint32 outputRate, int32 pitchWheel) {
- if (semiTone < 0)
- semiTone = 0;
- if (semiTone > 119)
- semiTone = 119;
- if (semiTone < 0)
- semiTone = 0;
- if (semiTone > 119)
- semiTone = 119;
-
- static const float noteFrq[] = {
- 0004.13f, 0004.40f, 0004.64f, 0004.95f, 0005.16f, 0005.50f, 0005.80f, 0006.19f, 0006.60f, 0006.86f,
- 0007.43f, 0007.73f, 0008.25f, 0008.80f, 0009.28f, 0009.90f, 0010.31f, 0011.00f, 0011.60f, 0012.38f,
- 0013.20f, 0013.75f, 0014.85f, 0015.47f, 0016.50f, 0017.60f, 0018.56f, 0019.80f, 0020.63f, 0022.00f,
- 0023.21f, 0024.75f, 0026.40f, 0027.50f, 0029.70f, 0030.94f, 0033.00f, 0035.20f, 0037.16f, 0039.60f,
- 0041.25f, 0044.00f, 0046.41f, 0049.50f, 0052.80f, 0055.00f, 0059.40f, 0061.88f, 0066.00f, 0070.40f,
- 0074.25f, 0079.20f, 0082.50f, 0088.00f, 0092.83f, 0099.00f, 0105.60f, 0110.00f, 0118.80f, 0123.75f,
- 0132.00f, 0140.80f, 0148.50f, 0158.40f, 0165.00f, 0176.00f, 0185.65f, 0198.00f, 0211.20f, 0220.00f,
- 0237.60f, 0247.50f, 0264.00f, 0281.60f, 0297.00f, 0316.80f, 0330.00f, 0352.00f, 0371.30f, 0396.00f,
- 0422.40f, 0440.00f, 0475.20f, 0495.00f, 0528.00f, 0563.20f, 0594.00f, 0633.60f, 0660.00f, 0704.00f,
- 0742.60f, 0792.00f, 0844.80f, 0880.00f, 0950.40f, 0990.00f, 1056.00f, 1126.40f, 1188.00f, 1267.20f,
- 1320.00f, 1408.00f, 1485.20f, 1584.00f, 1689.60f, 1760.00f, 1900.80f, 1980.00f, 2112.00f, 2252.80f,
- 2376.00f, 2534.40f, 2640.00f, 2816.00f, 2970.40f, 3168.00f, 3379.20f, 3520.00f, 3801.60f, 3960.00f
- };
-
- float pwModifier = (pitchWheel - 0x2000) / 0x2000;
- int8 d = pwModifier ? (pwModifier < 0 ? -1 : 1) : 0;
- float rateshift = (noteFrq[semiTone] - ((noteFrq[semiTone] -
- noteFrq[semiTone + d]) * pwModifier * d)) / noteFrq[semiToneRootkey];
-
- return (float)sampleRate * 10.0f * rateshift / outputRate;
+void SoundTowns::fadeOutSoundEffects() {
+ for (int i = 127; i > 0; i-= 12) {
+ _driver->chanVolume(0x46, i);
+ _driver->chanVolume(0x47, i);
+ _vm->delay(_vm->tickLength());
+ }
+ stopAllSoundEffects();
}
SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
@@ -4048,8 +383,10 @@ SoundPC98::~SoundPC98() {
}
bool SoundPC98::init() {
- _driver = new TownsPC98_OpnDriver(_mixer, TownsPC98_OpnDriver::OD_TYPE26);
- return _driver->init();
+ _driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
+ bool reslt = _driver->init();
+ updateVolumeSettings();
+ return reslt;
}
void SoundPC98::loadSoundFile(uint file) {
@@ -4122,6 +459,18 @@ void SoundPC98::playSoundEffect(uint8 track) {
_driver->loadSoundEffectData(_sfxTrackData, track);
}
+void SoundPC98::updateVolumeSettings() {
+ if (!_driver)
+ return;
+
+ bool mute = false;
+ _driver->setSoundEffectVolume(ConfMan.getInt("sfx_volume"));
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ _driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+ _driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
// KYRA 2
@@ -4136,8 +485,8 @@ SoundTownsPC98_v2::~SoundTownsPC98_v2() {
}
bool SoundTownsPC98_v2::init() {
- _driver = new TownsPC98_OpnDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
- TownsPC98_OpnDriver::OD_TYPE86 : TownsPC98_OpnDriver::OD_TOWNS);
+ _driver = new TownsPC98_AudioDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
+ TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns);
if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
_vm->checkCD();
@@ -4160,7 +509,9 @@ bool SoundTownsPC98_v2::init() {
_useFmSfx = true;
}
- return _driver->init();
+ bool reslt = _driver->init();
+ updateVolumeSettings();
+ return reslt;
}
void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
@@ -4235,7 +586,7 @@ void SoundTownsPC98_v2::beginFadeOut() {
}
int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8, bool) {
- static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
+ //static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
static const char patternHOF[] = "%s.PCM";
static const char patternLOL[] = "%s.VOC";
@@ -4256,7 +607,7 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle,
if (!src)
return 0;
- uint16 sfxRate = rates[READ_LE_UINT16(src)];
+ //uint16 sfxRate = rates[READ_LE_UINT16(src)];
src += 2;
bool compressed = (READ_LE_UINT16(src) & 1) ? true : false;
src += 2;
@@ -4296,9 +647,7 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle,
sfx[i] = cmd;
}
- uint32 outputRate = uint32(11025 * SoundTowns::calculatePhaseStep(0x3c, 0x3c, sfxRate, 11025, 0x2000));
-
- _currentSFX = Audio::makeRawStream(sfx, outsize, outputRate,
+ _currentSFX = Audio::makeRawStream(sfx, outsize, 11025,
Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h], _currentSFX);
if (handle)
@@ -4315,157 +664,18 @@ void SoundTownsPC98_v2::playSoundEffect(uint8 track) {
_driver->loadSoundEffectData(_sfxTrackData, track);
}
-// static resources
-
-const uint32 TownsPC98_OpnCore::_adtStat[] = {
- 0x00010001, 0x00010001, 0x00010001, 0x01010001,
- 0x00010101, 0x00010101, 0x00010101, 0x01010101,
- 0x01010101, 0x01010101, 0x01010102, 0x01010102,
- 0x01020102, 0x01020102, 0x01020202, 0x01020202,
- 0x02020202, 0x02020202, 0x02020204, 0x02020204,
- 0x02040204, 0x02040204, 0x02040404, 0x02040404,
- 0x04040404, 0x04040404, 0x04040408, 0x04040408,
- 0x04080408, 0x04080408, 0x04080808, 0x04080808,
- 0x08080808, 0x08080808, 0x10101010, 0x10101010
-};
-
-const uint8 TownsPC98_OpnCore::_detSrc[] = {
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
- 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
- 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
- 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
- 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
- 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a,
- 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14,
- 0x16, 0x16, 0x16, 0x16
-};
-
-const int TownsPC98_OpnCore::_ssgTables[] = {
- 0x01202A, 0x0092D2, 0x006B42, 0x0053CB, 0x003DF8, 0x003053, 0x0022DA, 0x001A8C,
- 0x00129B, 0x000DC1, 0x000963, 0x0006C9, 0x000463, 0x0002FA, 0x0001B6, 0x0000FB,
- 0x0193B6, 0x01202A, 0x00CDB1, 0x0092D2, 0x007D7D, 0x006B42, 0x005ECD, 0x0053CB,
- 0x00480F, 0x003DF8, 0x0036B9, 0x003053, 0x00290A, 0x0022DA, 0x001E6B, 0x001A8C,
- 0x001639, 0x00129B, 0x000FFF, 0x000DC1, 0x000B5D, 0x000963, 0x0007FB, 0x0006C9,
- 0x000575, 0x000463, 0x00039D, 0x0002FA, 0x000242, 0x0001B6, 0x00014C, 0x0000FB
-};
-
-const uint8 TownsPC98_OpnCore::_percussionData[] = {
- 0,24,1,192,1,216,2,128,4,88,23,64,27,152,1,128,29,24,2,128,31,152,0,128,136,128,128,128,0,136,97,103,153,139,34,163,72,195,27,69,1,154,137,35,8,51,169,122,164,75,133,203,81,146,168,121,185,68,202,8,33,237,49,177,12,133,140,17,160,42,161,10,0,137,176, 57,
- 233,41,160,136,235,65,177,137,128,26,164,28,3,157,51,137,1,152,113,161,40,146,115,192,56,5,169,66,161,56,1,50,145,59,39,168,97,1,160,57,7,153,50,153,32,2,25,129,32,20,186,66,129,24,153,164,142,130,169,153,26,242,138,217,9,128,204,58,209,172,40, 176, 141,
- 128,155,144,203,139,0,235,9,177,172,0,185,168,138,25,240,59,211,139,19,176,90,160,17,26,132,41,1,5,25,3,50,144,115,147,42,39,152,41,3,56,193,105,130,155,66,200,26,19,218,154,49,201,171,138,176,251,139,185,172,136,189,139,145,207,41,160,171,152, 186, 139,
- 186,141,128,218,171,51,217,170,56,163,12,4,155,81,147,42,37,152,32,54,136,49,50,48,37,32,69,0,17,50,50,83,2,16,68,20,8,66,4,154,84,145,24,33,24,32,17,18,145,32,22,168,49,163,1,33,50,184,115,129,25,66,1,24,67,2,80,35,40,53,2,65,51,19,67,37,0,52,35,49, 37,
- 34,49,37,17,52,17,35,35,35,34,32,49,33,152,34,145,24,24,128,138,128,184,9,177,171,168,185,155,152,172,155,186,172,185,172,155,186,173,153,202,187,185,202,170,171,202,186,169,170,170,171,139,154,171,153,154,169,10,168,154,128,168,154,0,153, 152, 136, 137,
- 128,153,0,152,8,128,137,0,136,136,8,9,8,9,8,24,153,128,136,153,144,0,161,138,1,169,136,128,160,168,152,153,138,137,154,153,153,154,153,170,168,170,185,168,169,154,169,171,153,169,170,153,152,154,153,137,169,137,136,144,152,144,128,128,144,129,129, 0, 33,
- 0,17,17,17,33,33,18,18,34,34,34,34,34,34,35,19,35,19,35,35,18,19,18,35,18,33,0,8,8,8,8,8,8,8,160,205,65,176,171,203,16,240,95,242,120,145,156,66,177,26,19,153,9,35,35,239,56,132,138,154,50,145,203,25,32,20,237,24,130,138,160,27,39,173,50,203,64,145, 139,
- 18,168,48,146,171,65,18,176,12,52,128,25,5,57,240,104,161,25,129,18,188,114,160,26,36,200,154,18,1,128,186,73,162,173,32,184,25,144,137,234,8,154,32,160,158,18,187,81,2,235,41,36,144,154,17,67,128,33,160,114,146,26,37,33,232,41,130,41,178,29,50, 251, 24,
- 1,153,138,160,76,179,155,11,0,38,252,41,146,41,178,27,193,43,39,170,136,17,129,8,49,233,48,129,11,6,26,130,136,128,64,1,248,105,145,9,16,144,140,5,25,168,16,186,48,5,171,217,57,134,171,8,34,188,20,203,41,6,155,161,89,164,140,2,136,51,202,41,131, 56, 144,
- 8,97,144,146,13,69,200,42,130,25,152,57,6,220,88,177,26,148,9,168,8,67,192,156,65,145,137,10,4,154,18,157,67,160,154,1,50,188,82,170,82,185,49,220,97,144,10,8,16,145,9,136,18,202,51,184,141,114,179,139,24,19,8,250,121,160,40,160,10,18,152,168,42,35, 216,
- 187,120,145,18,156,203,84,144,9,144,26,66,161,13,1,128,17,154,18,142,6,154,65,192,29,35,186,64,192,24,9,146,56,185,16,248,121,176,40,129,136,171,96,147,140,50,203,64,144,41,128,161,187,71,200,24,129,24,217,56,20,220,24,4,169,9,1,33,201,26,134,141,51,201,
- 25,16,33,235,32,144,33,153,169,99,160,11,3,136,58,210,33,203,48,163,17,219,128,140,38,8,184,141,50,131,159,33,128,153,25,18,153,88,242,43,3,9,136,157,53,202,40,145,25,2,204,105,146,156,66,152,8,153,33,128,129,136,153,50,186,55,188,51,249,64,178, 27, 128,
- 48,177,156,18,35,175,51,189,32,51,234,155,69,184,26,2,152,9,17,136,144,137,50,235,115,216,24,2,170,67,187,49,129,155,4,27,129,56,232,43,39,203,40,3,154,169,66,184,114,224,25,2,9,128,11,35,155,18,11,202,84,169,26,5,154,8,160,98,185,17,187,50, 23, 188, 33,
- 1,139,4,154,90,147,12,3,43,2,170,171,103,193,28,132,137,8,129,24,170,50,201,42,35,202,169,52,201,33,218,40,39,203,0,40,147,29,163,139,83,185,1,4,159,34,160,12,21,155,40,129,137,58,151,13,2,136,144,16,153,40,17,131,207,51,144,140,4,154,17,146,170,73, 163,
- 44,164,12,152,37,203,17,128,144,139,23,154,128,138,38,216,41,1,0,233,73,131,171,49,136,9,164,46,3,171,32,0,145,157,38,187,64,176,58,134,155,18,136,217,64,1,200,140,38,153,170,66,161,8,169,65,185,98,200,41,3,155,144,58,23,187,1,145,40,147,189,32, 68, 249,
- 1,112,255,199,195,19,108,76,187,247,247,183,40,168,212,245,199,227,68,45,59,10,145,177,198,24,130,76,26,193,180,129,0,162,42,160,199,162,0,16,152,137,132,168,195,130,162,181,227,163,161,179,211,180,179,164,128,162,161,194,164,179,40,153,195,213,146, 178,
- 147,176,50,186,161,196,151,58,16,28,162,160,131,122,155,33,241,146,128,40,26,128,154,36,170,89,59,9,24,144,77,161,8,177,112,139,33,232,148,24,41,61,9,26,162,32,30,58,153,32,59,73,59,11,79,137,57,9,49,30,24,153,131,25,106,61,153,73,28,56,27, 41, 137, 148,
- 76,43,74,58,13,161,3,171,149,32,77,10,74,42,168,16,0,123,138,129,162,178,225,50,140,161,0,147,10,129,41,244,210,165,1,152,24,162,184,166,32,144,59,216,132,177,8,145,67,143,146,160,183,162,130,24,192,32,225,146,144,33,44,73,30,129,137,32,76, 152, 25, 161,
- 2,154,32,177,132,232,2,136,210,128,149,177,32,58,27,168,225,133,8,44,107,136,25,136,17,26,58,46,16,11,145,17,144,79,136,144,136,145,152,33,31,162,130,200,82,153,74,137,147,26,0,13,133,170,149,16,192,0,178,0,128,152,182,150,9,16,9,137,33,59,63,10,152, 32,
- 179,192,5,154,228,182,145,130,144,42,128,242,2,136,41,168,17,76,57,31,129,136,17,47,8,41,138,32,138,123,59,58,10,136,161,4,46,25,145,136,129,25,56,28,91,41,154,108,9,16,44,24,137,48,15,0,194,162,41,194,56,241,163,146,0,139,7,186,150,129,152,1,208,33,176,
- 136,164,163,185,7,138,130,242,162,163,177,88,136,184,166,146,0,25,25,177,199,146,16,136,9,145,178,178,0,147,138,229,18,152,25,144,163,246,162,129,129,184,5,152,178,145,148,136,146,95,152,128,144,33,170,81,11,40,202,131,0,243,24,1,11,148,42, 24, 163, 140,
- 120,9,76,58,153,145,56,30,72,46,42,9,8,57,91,76,59,26,160,129,41,76,10,57,192,163,129,16,225,2,27,40,200,48,91,226,40,145,43,177,177,182,196,145,33,184,165,17,192,163,194,129,211,128,162,197,129,0,136,211,146,8,162,144,0,167,160,1,176,150,137,1, 24, 243,
- 0,129,145,25,123,169,130,168,132,41,63,42,136,137,120,26,136,8,24,89,29,58,177,193,147,1,26,162,176,167,180,8,49,28,29,178,162,88,43,42,57,43,61,8,29,129,128,128,123,137,24,243,16,136,16,46,0,169,149,128,1,60,153,72,154,90,25,25,25,8,91,73,12,16,137,144,
- 72,11,8,167,128,129,9,138,166,193,147,162,123,137,145,1,162,26,1,219,147,129,210,147,243,1,243,16,144,145,160,131,200,4,59,75,57,218,2,178,77,24,60,11,147,10,50,141,64,27,185,122,161,41,128,90,136,24,46,16,139,16,24,28,124,9,41,8,26,121,10,42,40,139,129,
- 0,201,135,137,56,176,176,35,215,145,1,26,145,144,160,135,138,1,177,146,146,161,65,242,136,164,177,1,1,186,151,208,148,129,10,32,241,145,163,178,17,168,136,151,168,2,148,185,133,176,130,129,154,163,215,0,146,136,40,211,161,131,171,81,144,170, 21, 184, 56,
- 195,168,133,177,91,16,187,5,145,153,66,172,18,177,42,120,138,27,134,26,106,42,138,146,184,66,75,46,41,168,0,145,57,91,75,27,24,27,48,169,40,122,9,109,10,8,177,146,16,74,30,129,160,162,146,41,124,138,24,145,152,3,1,14,3,139,1,192,161,151,177,122,8, 10, 0,
- 176,130,129,27,88,225,0,2,154,129,129,193,49,203,81,153,226,33,0,30,0,176,179,18,9,96,156,162,148,160,129,2,29,195,128,0,56,156,20,232,129,128,32,10,144,74,183,9,145,162,1,162,138,23,171,1,164,224,34,43,43,177,200,135,161,91,57,154,177,148, 145, 146, 58,
- 108,136,170,35,208,177,34,128,44,129,155,151,243,16,1,154,72,193,144,18,11,122,160,153,5,192,24,130,184,132,226,0,128,153,131,181,136,65,154,128,17,170,39,28,59,144,168,80,25,47,24,26,144,32,47,41,153,161,148,8,92,9,9,129,144,33,26,47,24,137,108, 25, 10,
- 17,10,73,75,47,24,184,48,8,45,57,138,136,150,10,48,139,136,35,203,121,8,27,179,161,106,0,29,16,176,179,3,185,19,227,41,145,168,61,197,177,20,10,57,42,250,147,196,16,41,138,24,195,208,135,137,0,145,160,2,210,146,195,177,132,136,153,167,210,146,162, 40, 8,
- 138,148,227,145,17,137,40,169,179,130,242,2,196,9,146,145,169,167,146,130,137,136,51,220,17,163,28,74,10,76,40,140,5,137,43,18,12,107,137,40,8,201,50,0,143,3,138,161,134,138,104,169,16,162,160,121,25,28,129,152,32,56,14,16,184,146,3,46,25, 176, 129, 179,
- 193,17,130,202,135,8,57,25,154,148,184,120,9,153,211,165,24,128,26,17,242,161,18,185,81,42,11,17,12,25,181,137,66,42,47,41,184,166,129,24,91,27,136,196,0,0,74,28,178,161,149,160,32,8,225,32,128,59,8,169,50,139,47,72,186,16,132,9,122,9,160,146,144,89,153,
- 10,149,178,0,121,11,146,152,162,48,13,123,177,24,0,106,27,9,144,132,12,17,0,168,0,181,56,169,129,242,195,129,17,154,64,161,244,16,137,24,144,144,164,129,75,42,176,149,9,179,148,203,4,166,136,163,128,227,163,8,57,11,30,165,0,74,59,62,9,208,131,144,40, 76,
- 26,27,196,129,1,25,43,49,174,67,153,136,106,152,41,25,28,2,43,44,104,45,59,8,43,128,144,120,25,12,17,152,9,130,155,151,145,74,40,13,48,192,58,90,43,43,177,146,49,31,75,24,217,131,0,76,26,152,149,161,24,74,154,193,166,145,32,27,161,164,176,135,152,24,193,
- 162,146,164,58,227,193,148,161,128,18,234,130,180,145,2,200,1,163,186,98,184,129,149,153,49,42,186,151,242,129,1,43,8,177,212,165,8,40,137,24,8,144,90,9,25,48,44,46,24,138,40,144,108,58,27,128,181,128,80,29,42,152,162,130,25,106,136,11,148,8,144,128,136,
- 112,139,80,153,24,136,129,46,0,60,129,208,1,3,13,57,168,144,1,242,17,9,26,2,185,27,55,140,73,137,179,16,192,3,145,143,33,9,171,135,160,17,137,10,151,168,3,178,44,17,208,144,167,0,40,155,16,167,152,18,144,26,160,199,1,136,91,136,160,178,150,161,1,10, 181,
- 145,161,1,145,161,198,2,9,90,137,177,160,150,40,29,129,144,145,162,57,77,169,16,148,42,42,40,141,34,170,121,154,210,131,162,107,8,9,160,195,40,73,139,18,224,162,34,139,0,244,178,163,24,26,146,194,166,49,29,42,137,130,192,16,93,128,154,19,59, 11, 122, 11,
- 146,177,120,42,26,43,164,152,17,60,63,137,128,48,10,58,92,9,59,91,75,139,32,25,25,61,74,28,177,40,130,74,29,73,168,130,128,48,14,8,77,9,25,26,179,211,32,78,26,41,152,161,180,89,59,9,153,166,160,3,26,57,106,154,88,184,40,1,27,58,73,143,131,169,3,161, 184,
- 122,152,16,181,145,129,17,15,129,193,147,145,192,33,193,162,183,163,136,178,129,178,197,2,41,216,131,168,163,181,226,163,178,1,33,187,166,212,129,1,27,24,162,184,151,8,16,160,144,181,210,72,168,128,32,42,25,40,142,5,185,88,58,11,58,177,32,129,63,42, 136,
- 186,53,29,75,58,144,144,129,77,128,11,144,133,29,40,152,24,161,129,80,155,60,3,12,89,8,60,152,152,49,136,47,57,224,129,16,41,90,139,162,147,170,51,169,27,17,95,26,26,160,5,139,48,76,10,228,146,1,136,44,161,147,209,130,137,73,224,1,162,195,32,210,177,180,
- 179,148,145,154,132,242,146,1,152,32,192,1,144,155,7,177,168,5,138,178,148,152,150,136,89,152,9,41,196,145,40,28,16,8,10,178,167,24,1,44,123,137,136,145,194,48,27,74,26,192,179,135,136,88,27,10,177,163,164,128,73,24,31,8,0,192,149,144,129,9,106, 41, 200,
- 161,151,41,138,0,24,226,162,49,42,11,90,136,136,152,17,145,10,63,40,11,56,245,162,16,26,73,11,144,135,137,58,106,10,25,8,57,137,28,33,129,156,113,10,10,161,18,8,153,77,3,217,0,1,242,128,193,18,128,75,60,178,154,37,45,58,29,144,1,184,66,41,29, 8, 145, 10,
- 194,33,148,170,107,89,139,128,163,178,16,63,59,176,144,151,129,42,74,10,129,192,2,128,154,97,192,0,177,128,178,183,16,16,155,149,145,184,84,138,8,192,161,20,225,0,130,138,165,0,28,148,153,18,209,128,88,153,89,152,9,17,9,29,130,43,122,153,24, 32, 202, 49,
- 24,43,106,154,130,193,27,51,29,28,133,138,65,11,123,25,10,40,152,44,130,26,43,148,45,73,140,33,8,153,88,128,61,144,42,59,225,128,18,155,50,75,186,20,202,120,144,42,92,176,162,165,25,2,169,152,135,185,19,152,8,146,160,123,195,137,132,209,0,16, 11, 2, 242,
- 146,164,152,73,193,136,130,178,1,136,169,23,169,128,164,242,129,178,129,32,138,180,167,153,132,8,138,2,209,4,138,1,128,138,92,136,44,129,136,162,33,63,40,141,2,160,144,106,137,64,155,17,129,60,30,146,26,17,28,48,46,169,51,154,91,137,41,26,32,143,18, 138,
- 1,32,28,123,177,9,181,195,56,57,14,145,161,17,17,31,41,152,145,194,194,20,153,41,9,243,129,180,0,128,45,16,43,170,135,144,16,25,42,137,242,163,194,16,0,57,14,130,194,178,16,33,30,8,59,211,163,160,5,137,44,10,17,170,3,120,9,44,146,136,131,140, 91, 9, 171,
- 7,161,32,73,13,8,161,40,106,11,25,129,59,0,49,31,42,28,40,11,0,81,176,61,32,138,25,178,241,148,136,106,8,136,128,177,90,8,155,96,176,9,18,217,132,129,10,81,156,40,178,161,36,169,76,147,203,150,0,10,146,200,147,149,128,144,148,154,182,24,0,137,11,134,211,
- 24,136,129,145,209,33,8,43,163,243,88,41,13,0,160,145,33,31,32,185,145,4,155,17,32,47,161,128,73,160,44,56,176,75,74,12,35,141,104,137,9,89,152,58,56,44,41,30,41,40,157,48,128,154,88,41,42,8,14,3,184,59,120,152,9,56,10,128,41,57,227,186,52,152,62, 8, 56,
- 242,0,58,8,156,34,243,128,24,176,51,169,58,183,192,146,164,177,18,170,7,177,208,132,161,24,136,27,147,243,128,133,10,24,161,161,178,214,17,160,25,16,161,137,165,192,48,27,72,58,218,133,162,26,72,27,10,197,178,49,138,89,56,142,1,24,11,0,44,105, 10, 25, 0,
- 194,9,3,47,8,138,147,18,28,48,202,147,199,146,25,161,0,145,194,163,57,11,146,248,130,32,57,63,154,16,48,14,128,144,209,133,26,56,154,182,162,195,18,152,44,194,180,168,5,24,137,138,35,192,232,66,176,161,24,41,26,244,129,163,160,75,129,226,147,40, 145, 61,
- 13,130,177,17,137,112,170,130,0,136,75,152,177,241,34,0,59,156,51,186,178,91,132,137,137,122,1,45,28,50,172,57,108,8,26,136,32,152,46,144,131,171,4,152,18,141,148,1,216,32,9,60,169,66,152,128,72,90,201,1,17,201,136,3,195,26,73,133,200,176, 150, 146, 169,
- 24,33,178,184,151,73,11,28,72,44,153,82,153,17,42,57,78,153,8,160,0,1,123,11,19,171,195,18,59,31,129,10,162,2,58,96,142,130,26,75,128,176,17,180,123,9,90,137,211,145,32,26,76,43,145,130,12,90,41,27,58,160,160,128,178,7,76,59,0,203,180,147,33,62,10,0,243,
- 129,146,73,29,145,144,0,26,56,153,185,83,8,76,27,166,161,193,146,131,224,145,165,161,40,168,149,162,226,2,136,138,163,131,211,0,59,146,218,148,1,192,16,16,58,248,88,144,177,136,1,58,45,9,195,197,147,48,29,10,0,162,176,64,122,9,10,17,9,153,56, 75, 27, 31,
- 72,136,9,129,129,61,45,59,10,161,18,122,43,59,41,169,34,155,130,131,219,120,162,27,49,208,160,131,156,66,12,145,50,240,16,136,12,162,40,129,130,15,129,162,146,180,83,139,58,217,129,177,4,0,169,197,163,144,242,131,168,179,179,17,197,145,178,164, 128, 160,
- 211,2,244,163,145,162,129,212,177,163,17,208,163,195,180,57,24,170,182,164,129,0,60,60,169,149,162,177,122,26,24,136,136,133,43,27,178,56,77,24,128,240,0,2,44,46,8,128,193,146,64,27,42,16,193,25,0,192,148,11,52,47,153,147,243,0,24,73,28,144, 161, 150, 9,
- 8,73,170,2,162,25,27,147,167,131,29,1,168,200,165,16,91,137,8,162,176,35,41,31,24,169,50,168,58,123,144,48,128,13,73,169,144,16,57,123,44,200,163,56,153,80,10,176,146,57,94,8,152,131,9,168,125,26,145,177,132,137,41,60,26,144,243,32,192,34,60, 43, 26, 16,
- 249,164,16,58,61,11,130,243,146,2,42,44,27,128,165,137,49,45,28,16,43,8,211,48,28,152,105,9,9,163,161,169,35,107,42,232,164,130,168,72,42,168,210,148,144,136,129,3,217,194,50,27,192,41,210,147,40,76,226,1,161,1,155,132,145,147,171,67,173,210,132,161,106,
- 137,56,169,209,131,64,13,129,9,194,17,57,61,169,17,128,40,31,16,10,162,57,61,75,139,40,242,17,58,59,138,179,144,50,105,140,179,243,57,40,26,9,243,130,24,29,57,128,210,129,25,59,91,137,162,178,72,27,181,168,19,129,8,184,231,147,178,32,28,184,198,148, 144,
- 1,26,128,16,192,2,26,144,244,129,0,16,10,197,177,181,1,41,9,178,165,211,129,25,145,137,210,147,152,210,163,132,194,17,91,169,145,181,130,9,89,137,152,178,4,128,9,63,160,128,106,8,25,43,10,32,47,26,123,152,24,40,25,27,18,186,35,158,64,42,216,33,25,58, 58,
- 45,184,147,29,72,46,9,0,178,146,58,77,26,25,209,165,128,145,17,153,128,129,148,240,129,1,40,31,0,152,242,163,16,59,44,24,243,146,128,1,26,26,179,213,145,130,176,131,40,25,145,219,179,167,8,33,59,14,176,166,16,136,74,128,176,128,149,8,8,209,148,152,0, 72,
- 153,161,178,35,62,75,154,163,153,19,62,170,133,179,136,89,12,129,164,144,3,47,58,193,177,148,0,61,43,10,129,17,41,61,43,25,8,126,26,25,137,145,34,44,45,129,216,179,1,90,25,137,32,227,8,16,9,170,49,31,32,29,128,145,148,75,25,75,153,162,192,35,12, 80, 136,
- 176,8,194,24,1,176,21,154,145,80,251,130,2,30,9,8,130,145,128,98,27,26,129,136,162,15,33,168,59,65,177,77,141,1,128,168,113,10,137,178,163,146,132,74,153,224,164,33,184,19,184,228,161,17,91,152,25,146,152,44,121,9,160,145,17,25,28,93,128,152,2,25,27,161,
- 210,129,146,45,179,227,163,162,9,40,193,148,179,57,107,140,196,32,25,57,47,136,210,130,24,40,28,152,210,182,145,40,8,129,184,147,147,140,163,166,160,34,45,144,194,161,134,41,46,152,162,162,3,44,58,75,209,162,144,57,129,47,152,130,59,16,248,129,17,26, 57,
- 9,29,167,2,60,42,138,136,209,130,90,42,42,176,146,178,120,28,8,160,145,16,33,31,1,8,160,129,128,242,164,32,152,177,146,213,196,128,40,26,160,163,180,146,108,60,144,144,136,147,137,40,90,161,3,17,219,243,33,184,130,60,136,243,178,179,132,26,8,168,212,147,
- 16,57,42,31,145,145,160,32,43,184,66,45,180,33,140,226,1,91,152,16,144,193,162,48,77,25,137,153,17,178,78,0,0,16,14,90,152,153,19,129,13,123,137,129,160,1,73,44,9,129,0,153,120,10,9,162,195,32,139,28,151,161,2,128,26,45,193,146,48,29,146,153, 194, 5, 59,
- 29,128,144,195,1,64,43,208,178,149,8,9,16,240,163,129,16,42,185,181,211,24,48,45,137,149,9,24,41,75,184,177,4,43,91,128,180,16,144,29,25,184,167,1,59,60,153,148,161,146,91,42,186,4,24,145,123,11,2,178,77,136,26,25,195,40,115,61,27,168,177,3,59,79,26, 25,
- 144,1,48,13,56,154,248,1,16,9,129,8,2,178,31,130,153,162,20,15,33,170,56,40,29,28,128,152,149,144,56,120,11,162,212,129,144,145,59,180,243,147,145,144,16,152,48,241,0,161,176,1,134,10,129,200,166,144,128,121,26,24,177,178,196,48,75,138,41,180,195,26, 24,
- 89,138,24,33,187,41,84,155,57,79,136,160,210,130,0,58,58,168,243,132,27,41,75,138,3,8,61,8,29,145,179,76,24,28,146,208,2,49,140,75,196,144,0,40,44,179,208,3,176,33,15,177,2,160,106,8,160,164,164,8,73,27,226,179,161,1,57,1,196,211,128,40,156,145,166, 178,
- 131,29,128,145,162,165,40,27,216,146,135,144,40,160,194,177,145,20,139,200,151,178,17,136,40,25,205,130,17,11,17,129,156,38,26,25,137,179,163,11,79,16,12,146,147,143,89,25,136,136,25,48,26,46,129,40,29,42,29,8,145,2,56,27,62,8,25,212,161,48,43, 144, 129,
- 29,145,144,41,106,10,107,43,184,131,1,36,61,13,138,2,194,1,16,27,75,186,181,151,8,1,161,138,211,129,2,59,248,129,16,0,144,63,152,150,136,24,25,128,30,161,128,17,24,225,146,10,16,0,9,227,183,129,40,60,26,162,194,181,24,90,9,24,0,176,161,193,194,35,12, 63,
- 8,210,162,1,32,78,28,152,164,144,16,48,45,137,162,147,168,152,98,27,43,33,12,160,165,129,137,63,41,153,153,151,16,91,26,8,8,9,56,10,46,24,146,57,168,160,166,241,129,32,140,16,145,179,164,137,113,138,208,131,26,25,1,42,178,196,106,24,171,18,196,8, 18, 29,
- 41,194,128,3,249,57,162,152,48,184,120,160,208,33,137,74,57,187,149,129,26,35,158,72,128,168,32,26,25,180,75,2,136,15,163,161,136,120,27,41,160,128,182,56,60,25,12,178,151,128,168,72,10,152,4,177,26,147,137,113,44,42,33,220,2,152,41,82,11, 210, 163, 184,
- 133,162,10,196,128,3,234,40,149,152,161,1,44,129,194,4,225,16,58,168,24,194,146,146,154,49,21,218,33,152,248,129,194,147,0,28,1,195,162,20,140,42,25,160,198,1,33,136,142,3,25,24,141,16,177,208,112,0,138,41,160,130,45,60,32,170,73,24,75,59,161,176,49,159,
- 97,26,168,149,145,32,28,25,184,211,129,179,74,73,8,153,136,193,151,160,32,48,143,9,147,181,145,32,60,9,187,133,166,144,32,152,25,136,161,150,168,145,81,10,42,0,169,182,148,136,58,41,187,182,211,131,16,137,25,243,144,129,2,9,8,202,7,25,185,21,144,136,153,
- 65,184,137,56,151,10,153,49,16,145,14,56,176,11,192,19,89,91,44,168,147,2,8,147,63,27,1,136,229,129,73,26,136,26,137,81,170,147,77,72,12,42,42,192,24,104,91,26,27,65,177,27,32,41,60,14,136,17,170,150,129,24,58,11,16,251,162,19,57,31,0,152,129,145,17, 61,
- 14,1,129,27,129,66,169,178,74,12,11,19,198,145,75,33,138,174,133,1,184,57,40,136,169,20,1,60,174,20,154,201,67,26,162,151,42,16,138,59,130,204,20,169,59,180,59,114,184,56,178,242,128,130,43,8,194,3,229,144,33,185,144,34,181,145,168,17,149,153,74,35, 220,
- 129,128,1,88,59,75,225,136,130,168,17,144,12,151,8,25,179,8,1,240,16,8,25,145,211,41,130,138,115,169,160,163,168,84,154,74,0,170,144,211,149,2,30,128,137,9,149,1,144,58,60,57,153,178,150,17,29,27,74,25,195,152,56,15,1,25,26,152,149,80,153,57,73,140, 128,
- 160,144,113,27,56,28,25,4,42,44,137,60,171,130,50,240,8,5,139,145,1,105,137,200,80,137,145,146,178,179,160,46,16,240,195,131,128,144,24,164,198,128,0,136,137,131,194,165,177,2,161,147,11,144,188,181,148,144,23,0,28,224,128,131,192,32,1,224,1,168,132,145,
- 9,41,208,58,137,179,151,145,16,1,30,8,145,178,1,47,32,186,72,169,146,75,8,41,48,136,89,13,48,9,10,124,26,11,42,32,129,91,77,16,12,128,42,57,138,10,60,2,63,9,0,93,128,152,90,8,10,24,40,44,144,29,49,188,48,72,25,30,177,33,128,186,120,129,186,133, 152, 130,
- 24,156,51,154,8,226,2,56,155,2,179,233,167,128,24,129,176,136,151,8,184,0,33,224,152,21,177,24,10,163,16,250,17,130,171,83,137,136,37,12,56,242,154,17,160,145,82,13,3,201,128,18,137,24,162,63,162,8,107,178,128,57,158,32,24,200,18,0,106,154,73,16, 248, 8,
- 73,137,57,75,0,128,12,65,137,59,75,28,144,129,122,0,58,140,160,195,145,105,56,28,153,145,164,88,8,28,25,153,9,162,113,89,153,136,33,234,147,128,41,72,11,138,151,144,145,16,43,58,248,130,178,42,4,40,10,196,154,147,216,24,7,136,10,161,148,210,161, 98, 138,
- 137,128,146,176,33,105,27,43,163,49,185,6,10,136,43,67,174,161,162,151,137,1,64,200,193,24,64,200,56,145,242,24,57,137,1,128,3,162,175,80,128,162,152,25,58,175,17,17,0,200,64,168,162,91,1,154,44,211,177,35,64,160,161,144,4,241,41,209,162,25,1,3,242, 176,
- 134,153,42,41,136,135,154,2,130,46,41,161,153,180,145,34,26,46,18,242,137,146,129,25,128,11,151,161,40,179,27,122,168,59,137,181,50,172,36,56,15,9,129,137,128,75,2,58,12,52,141,8,24,58,153,157,122,145,9,1,80,27,184,32,74,219,50,57,168,153,180,48,28, 143,
- 131,144,178,65,13,48,168,162,147,155,121,9,170,5,16,153,21,29,144,161,91,0,184,57,128,137,17,159,88,178,128,105,152,9,162,33,164,141,88,178,224,1,0,16,27,185,150,161,9,4,139,16,128,160,194,144,65,180,46,40,136,27,135,160,16,44,57,145,236,2,195,40,75,177,
- 2,200,179,146,186,104,50,141,24,169,165,148,11,97,10,11,130,177,49,57,78,42,154,128,165,59,33,28,30,1,136,16,192,41,128,152,123,136,24,1,169,113,10,11,49,153,14,147,19,45,43,8,176,210,148,8,16,11,96,144,192,163,150,10,128,43,26,150,178,165,24,41,171, 18,
- 27,215,1,8,128,136,40,35,208,11,161,193,18,73,154,133,155,165,164,10,49,154,8,199,0,2,168,64,192,0,40,162,43,202,180,150,10,106,24,185,145,131,184,113,43,24,162,187,73,146,42,81,171,121,58,155,151,16,43,32,31,9,160,146,17,136,94,10,24,145,25, 9, 130, 59,
- 65,13,91,25,169,146,176,112,42,59,16,217,130,20,13,25,9,40,161,138,68,169,154,18,62,154,180,145,135,152,56,58,155,165,211,8,40,42,10,198,1,2,184,57,184,224,51,154,27,134,168,19,202,73,75,184,35,176,75,24,25,209,51,157,19,30,184,179,3,33,148,45, 232, 146,
- 129,168,41,32,170,149,193,35,136,16,50,191,56,146,173,149,16,24,41,30,129,168,209,3,57,31,0,16,176,147,41,152,10,17,181,14,40,144,49,170,75,97,141,25,162,146,72,177,92,137,137,19,137,153,113,154,2,41,60,129,217,2,211,152,73,42,193,197,146,147, 10, 59, 0,
- 192,196,132,41,160,25,88,169,16,40,241,1,153,81,28,10,147,161,209,88,75,9,161,162,180,16,43,57,235,33,56,156,129,144,2,135,31,128,145,136,163,56,59,154,57,167,160,105,137,0,138,163,3,41,47,185,211,131,41,41,60,139,182,146,16,16,43,242,144,145,129,16,179,
- 183,1,26,9,147,240,131,160,91,74,152,184,166,178,33,140,9,4,162,233,34,136,129,144,163,60,142,144,149,128,33,73,13,161,194,131,0,26,56,142,128,163,128,1,233,56,209,41,145,194,147,179,149,64,30,8,128,216,18,24,43,43,32,153,25,74,109,137,153,48,8,137, 122,
- 25,144,26,43,59,30,33,41,27,24,96,153,160,50,76,27,47,152,145,163,73,40,14,152,131,176,74,90,8,8,200,67,155,154,50,49,155,28,124,177,152,1,2,17,62,138,180,176,4,25,9,177,245,162,129,40,25,176,164,130,172,4,8,181,194,49,11,168,154,165,133,152,40,136, 226,
- 179,19,26,185,16,167,194,16,25,57,243,136,147,1,31,25,184,132,160,33,62,138,129,130,41,121,137,153,145,26,17,107,136,179,1,61,60,26,162,168,148,64,31,25,32,168,152,64,31,137,8,129,33,62,24,137,8,16,59,47,153,33,162,91,59,41,170,145,5,43,60,41,13,178,134,
- 57,153,12,194,227,8,2,128,57,208,162,19,216,32,178,25,128,160,48,194,195,37,155,10,33,251,163,146,16,136,12,166,195,160,148,129,176,147,178,150,160,72,162,162,193,162,60,200,145,5,144,25,122,216,129,161,130,0,10,73,1,241,2,9,168,33,13,161,165,24,64, 203,
- 50,1,14,9,9,129,161,106,33,27,13,164,128,40,41,107,169,160,33,136,60,92,168,152,2,91,57,176,129,0,144,47,136,162,164,128,80,43,154,179,213,130,74,27,0,145,145,167,58,59,160,9,26,76,8,171,5,49,28,44,169,162,183,130,72,28,144,179,228,2,25,26,129, 186, 151,
- 1,75,128,169,17,178,15,57,170,16,166,16,57,8,139,162,181,1,8,152,164,181,41,81,43,10,242,145,57,139,89,8,193,18,154,32,176,10,165,129,137,147,177,134,0,25,25,201,147,227,129,72,59,185,167,128,129,160,91,25,176,130,147,145,9,160,5,202,17,16, 186, 136, 37,
- 177,56,76,42,169,186,48,9,145,57,24,128,41,169,134,137,145,147,28,41,168,131,228,32,27,9,60,129,178,64,60,45,25,9,24,152,49,31,136,57,42,0,25,12,181,18,153,57,96,169,177,132,153,123,9,152,129,177,17,74,43,24,169,128,121,137,25,1,139,96,42,10,146,178, 18,
- 44,29,1,161,164,146,31,137,146,177,19,1,10,26,209,165,146,43,40,138,240,130,18,144,25,40,212,1,58,11,152,196,147,10,74,26,152,225,130,146,58,60,210,145,16,148,16,185,192,18,44,42,57,199,162,1,9,87,47,186,215,231,197,179,180,195,212,164,32,59,92, 126, 62,
- 41,59,76,59,60,168,179,213,197,163,72,44,25,74,126,127,127,79,26,177,148,90,27,225,247,165,0,152,147,123,138,211,164,72,126,127,46,210,196,163,228,215,64,11,210,180,1,8,58,153,1,224,149,57,76,27,24,76,42,43,136,128,243,179,130,106,60,42,42,92,28,243,231,
- 147,24,57,44,58,94,45,8,57,139,214,148,40,77,26,9,16,10,144,64,62,43,25,123,59,138,162,48,63,26,41,92,60,43,176,3,59,232,214,164,16,75,75,76,60,153,179,33,62,26,136,40,75,169,197,163,129,57,60,59,75,138,145,64,63,138,179,1,42,136,90,43,176,214,180,1, 25,
- 152,195,129,129,106,76,60,137,145,178,2,25,10,228,130,57,59,44,41,154,165,105,76,44,144,16,76,26,41,76,26,152,1,58,26,9,193,165,16,92,26,41,77,59,76,76,60,26,136,161,130,152,195,163,211,146,0,57,11,211,130,8,25,40,62,153,162,17,109,60,153,146,40, 76, 60,
- 26,160,179,211,163,32,60,42,153,179,194,199,130,24,58,43,58,27,128,161,195,129,226,196,147,90,59,75,44,136,128,145,160,148,123,59,42,26,41,26,57,27,192,215,147,57,59,27,161,145,213,130,106,76,43,9,144,162,129,177,181,130,136,194,146,40,10,129,25,210,146,
- 178,197,196,179,196,130,8,41,9,144,178,130,209,182,17,92,43,176,147,144,212,130,136,0,177,130,73,62,10,161,130,91,75,59,43,57,46,25,41,77,10,177,164,16,26,136,210,197,179,130,128,57,77,43,25,75,10,227,179,180,179,146,128,57,185,183,163,145,0,8,8,10, 119,
- 114,120,16,210,244,60,28,41,25,152,149,56,161,35,44,89,27,24,136,24,164,211,17,233,176,136,192,129,179,17,17,25,0,10,46,160,132,49,66,24,132,177,147,193,56,72,26,29,232,168,176,12,137,41,139,147,9,1,41,15,91,136,35,148,21,18,48,40,1,168,167,144,0,42,172,
- 177,204,193,155,232,152,152,26,152,41,146,17,6,4,65,34,35,135,4,16,32,9,24,186,176,0,250,153,204,186,173,154,153,177,3,65,41,34,145,134,35,65,98,49,50,50,2,33,169,138,155,175,170,172,204,192,138,234,136,155,136,10,32,18,5,52,48,24,162,17,67,54,66,51, 34,
- 131,184,174,234,153,10,9,40,0,152,251,168,142,154,9,16,33,49,33,128,154,170,156,34,54,54,33,68,0,1,136,201,137,26,88,48,35,99,8,152,189,189,187,155,171,16,24,130,145,188,175,203,144,49,115,67,67,50,19,2,1,0,0,130,131,1,136,206,216,188,203, 204, 187, 187,
- 156,153,0,0,51,17,34,24,112,20,69,67,67,34,19,0,136,169,185,137,186,232,185,219,201,203,187,173,170,154,153,129,131,6,2,19,49,49,21,65,19,53,51,83,34,16,168,201,154,172,156,138,0,1,24,201,233,186,204,186,171,137,3,37,48,24,128,201,202,202,129,17, 48, 21,
- 22,20,19,19,32,16,2,66,52,68,4,3,1,203,235,188,189,186,171,153,137,153,170,219,170,140,9,17,53,115,50,52,67,51,51,51,17,130,0,145,154,169,188,236,187,190,203,187,172,171,138,136,17,33,18,2,34,98,98,50,50,52,66,34,35,2,19,24,169,203,203,188,219, 169, 154,
- 9,137,171,204,188,203,184,136,34,83,50,33,153,184,170,170,152,40,57,19,36,50,50,18,35,17,2,49,49,66,66,66,34,17,168,233,202,202,170,171,170,186,219,203,188,188,154,138,25,33,68,52,68,67,67,36,51,36,18,17,17,136,8,170,176,202,188,206,202,171,172,186, 169,
- 153,8,25,144,128,1,34,68,52,68,51,52,34,49,18,34,2,144,136,155,140,187,186,186,154,154,185,185,153,9,9,0,24,0,128,144,168,169,170,154,154,153,9,8,16,8,0,144,19,35,68,51,52,67,51,66,34,50,33,1,144,185,186,172,204,187,188,173,172,186,172,186, 154, 138, 41,
- 33,52,53,83,50,51,52,52,37,34,34,18,16,144,152,154,187,219,203,188,173,186,186,186,170,154,153,138,144,16,17,67,82,50,51,21,34,19,33,2,18,33,1,8,153,169,153,153,136,128,0,136,154,153,153,8,8,1,16,0,169,170,187,171,171,154,153,153,152,153,153,0,16,51, 83,
- 66,50,67,50,51,67,51,52,35,18,136,186,219,187,189,186,171,187,173,187,188,187,203,138,9,16,33,50,52,53,67,67,147,8,128,128,128,128,128,128,128,128,0,240,255,55,232,23,220,0,148,1,9,18,148,10,189,32,163,62,160,5,137,12,149,42,153,144,34,42,8, 1, 138, 181,
- 45,136,18,144,105,138,1,160,14,128,132,145,186,37,138,41,192,48,145,46,160,33,44,24,225,16,13,132,136,137,16,148,25,170,194,82,152,136,91,24,42,169,33,233,131,179,24,185,149,16,57,172,164,18,10,211,160,147,211,33,138,243,129,16,41,193,0,43, 132, 155, 73,
- 58,145,244,145,43,35,9,171,16,110,25,8,28,74,162,128,26,27,82,45,136,153,18,8,136,8
-};
+void SoundTownsPC98_v2::updateVolumeSettings() {
+ if (!_driver)
+ return;
+
+ bool mute = false;
+ _driver->setSoundEffectVolume(ConfMan.getInt("sfx_volume"));
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ _driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+ _driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
+}
} // End of namespace Kyra
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
index 2f2acd78d1..4b71b1d69d 100644
--- a/engines/kyra/staticres.cpp
+++ b/engines/kyra/staticres.cpp
@@ -27,25 +27,22 @@
#include "common/md5.h"
#include "kyra/kyra_v1.h"
#include "kyra/kyra_lok.h"
-#include "kyra/lol.h"
#include "kyra/kyra_v2.h"
#include "kyra/kyra_hof.h"
#include "kyra/kyra_mr.h"
#include "kyra/screen.h"
#include "kyra/screen_lok.h"
-#include "kyra/screen_lol.h"
#include "kyra/screen_hof.h"
#include "kyra/screen_mr.h"
#include "kyra/resource.h"
#include "kyra/gui_lok.h"
-#include "kyra/gui_lol.h"
#include "kyra/gui_hof.h"
#include "kyra/gui_mr.h"
#include "kyra/sound_intern.h"
namespace Kyra {
-#define RESFILE_VERSION 70
+#define RESFILE_VERSION 72
namespace {
bool checkKyraDat(Common::SeekableReadStream *file) {
@@ -305,36 +302,6 @@ const ItemAnimData_v2 *StaticResource::loadShapeAnimData_v2(int id, int &entries
return (const ItemAnimData_v2 *)getData(id, k2ShpAnimDataV2, entries);
}
-#ifdef ENABLE_LOL
-const LoLCharacter *StaticResource::loadCharData(int id, int &entries) {
- return (const LoLCharacter *)getData(id, kLolCharData, entries);
-}
-
-const SpellProperty *StaticResource::loadSpellData(int id, int &entries) {
- return (const SpellProperty *)getData(id, kLolSpellData, entries);
-}
-
-const CompassDef *StaticResource::loadCompassData(int id, int &entries) {
- return (const CompassDef *)getData(id, kLolCompassData, entries);
-}
-
-const FlyingObjectShape *StaticResource::loadFlyingObjectData(int id, int &entries) {
- return (const FlyingObjectShape *)getData(id, kLolFlightShpData, entries);
-}
-
-const uint16 *StaticResource::loadRawDataBe16(int id, int &entries) {
- return (const uint16 *)getData(id, kLolRawDataBe16, entries);
-}
-
-const uint32 *StaticResource::loadRawDataBe32(int id, int &entries) {
- return (const uint32 *)getData(id, kLolRawDataBe32, entries);
-}
-
-const ButtonDef *StaticResource::loadButtonDefs(int id, int &entries) {
- return (const ButtonDef *)getData(id, kLolButtonData, entries);
-}
-#endif // ENABLE_LOL
-
bool StaticResource::prefetchId(int id) {
if (id == -1) {
for (DataMap::const_iterator i = _dataTable.begin(); i != _dataTable.end(); ++i) {
@@ -641,160 +608,6 @@ bool StaticResource::loadShapeAnimData_v2(Common::SeekableReadStream &stream, vo
return true;
}
-#ifdef ENABLE_LOL
-bool StaticResource::loadCharData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
- size = stream.size() / 130;
- LoLCharacter *charData = new LoLCharacter[size];
-
- for (int i = 0; i < size; i++) {
- LoLCharacter *t = &charData[i];
-
- t->flags = stream.readUint16LE();
- stream.read(t->name, 11);
- t->raceClassSex = stream.readByte();
- t->id = stream.readSint16LE();
- t->curFaceFrame = stream.readByte();
- t->tempFaceFrame = stream.readByte();
- t->screamSfx = stream.readByte();
- stream.readUint32LE();
- for (int ii = 0; ii < 8; ii++)
- t->itemsMight[ii] = stream.readUint16LE();
- for (int ii = 0; ii < 8; ii++)
- t->protectionAgainstItems[ii] = stream.readUint16LE();
- t->itemProtection = stream.readUint16LE();
- t->hitPointsCur = stream.readSint16LE();
- t->hitPointsMax = stream.readUint16LE();
- t->magicPointsCur = stream.readSint16LE();
- t->magicPointsMax = stream.readUint16LE();
- t->field_41 = stream.readByte();
- t->damageSuffered = stream.readUint16LE();
- t->weaponHit = stream.readUint16LE();
- t->totalMightModifier = stream.readUint16LE();
- t->totalProtectionModifier = stream.readUint16LE();
- t->might = stream.readUint16LE();
- t->protection = stream.readUint16LE();
- t->nextAnimUpdateCountdown = stream.readSint16LE();
- for (int ii = 0; ii < 11; ii++)
- t->items[ii] = stream.readUint16LE();
- for (int ii = 0; ii < 3; ii++)
- t->skillLevels[ii] = stream.readByte();
- for (int ii = 0; ii < 3; ii++)
- t->skillModifiers[ii] = stream.readByte();
- for (int ii = 0; ii < 3; ii++)
- t->experiencePts[ii] = stream.readUint32LE();
- for (int ii = 0; ii < 5; ii++)
- t->characterUpdateEvents[ii] = stream.readByte();
- for (int ii = 0; ii < 5; ii++)
- t->characterUpdateDelay[ii] = stream.readByte();
- };
-
- ptr = charData;
- return true;
-}
-
-bool StaticResource::loadSpellData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
- size = stream.size() / 28;
- SpellProperty *spellData = new SpellProperty[size];
-
- for (int i = 0; i < size; i++) {
- SpellProperty *t = &spellData[i];
-
- t->spellNameCode = stream.readUint16LE();
- for (int ii = 0; ii < 4; ii++)
- t->mpRequired[ii] = stream.readUint16LE();
- t->field_a = stream.readUint16LE();
- t->field_c = stream.readUint16LE();
- for (int ii = 0; ii < 4; ii++)
- t->hpRequired[ii] = stream.readUint16LE();
- t->field_16 = stream.readUint16LE();
- t->field_18 = stream.readUint16LE();
- t->flags = stream.readUint16LE();
- };
-
- ptr = spellData;
- return true;
-}
-
-bool StaticResource::loadCompassData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
- size = stream.size() / 4;
- CompassDef *defs = new CompassDef[size];
-
- for (int i = 0; i < size; i++) {
- CompassDef *t = &defs[i];
- t->shapeIndex = stream.readByte();
- t->x = stream.readByte();
- t->y = stream.readByte();
- t->flags = stream.readByte();
- };
-
-
- ptr = defs;
- return true;
-}
-
-bool StaticResource::loadFlyingObjectData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
- size = stream.size() / 5;
- FlyingObjectShape *defs = new FlyingObjectShape[size];
-
- for (int i = 0; i < size; i++) {
- FlyingObjectShape *t = &defs[i];
- t->shapeFront = stream.readByte();
- t->shapeBack = stream.readByte();
- t->shapeLeft = stream.readByte();
- t->drawFlags = stream.readByte();
- t->flipFlags = stream.readByte();
- };
-
- ptr = defs;
- return true;
-}
-
-bool StaticResource::loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size) {
- size = stream.size() >> 1;
-
- uint16 *r = new uint16[size];
-
- for (int i = 0; i < size; i++)
- r[i] = stream.readUint16BE();
-
- ptr = r;
- return true;
-}
-
-bool StaticResource::loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size) {
- size = stream.size() >> 2;
-
- uint32 *r = new uint32[size];
-
- for (int i = 0; i < size; i++)
- r[i] = stream.readUint32BE();
-
- ptr = r;
- return true;
-}
-
-bool StaticResource::loadButtonDefs(Common::SeekableReadStream &stream, void *&ptr, int &size) {
- size = stream.size() / 18;
-
- ButtonDef *r = new ButtonDef[size];
-
- for (int i = 0; i < size; i++) {
- r[i].buttonflags = stream.readUint16BE();
- r[i].keyCode = stream.readUint16BE();
- r[i].keyCode2 = stream.readUint16BE();
- r[i].x = stream.readSint16BE();
- r[i].y = stream.readSint16BE();
- r[i].w = stream.readUint16BE();
- r[i].h = stream.readUint16BE();
- r[i].index = stream.readUint16BE();
- r[i].screenDim = stream.readUint16BE();
- }
-
- ptr = r;
- return true;
-}
-#endif // ENABLE_LOL
-
void StaticResource::freeRawData(void *&ptr, int &size) {
uint8 *data = (uint8 *)ptr;
delete[] data;
@@ -870,58 +683,6 @@ void StaticResource::freeHofShapeAnimDataV2(void *&ptr, int &size) {
size = 0;
}
-#ifdef ENABLE_LOL
-void StaticResource::freeCharData(void *&ptr, int &size) {
- LoLCharacter *d = (LoLCharacter *)ptr;
- delete[] d;
- ptr = 0;
- size = 0;
-}
-
-void StaticResource::freeSpellData(void *&ptr, int &size) {
- SpellProperty *d = (SpellProperty *)ptr;
- delete[] d;
- ptr = 0;
- size = 0;
-}
-
-void StaticResource::freeCompassData(void *&ptr, int &size) {
- CompassDef *d = (CompassDef *)ptr;
- delete[] d;
- ptr = 0;
- size = 0;
-}
-
-void StaticResource::freeFlyingObjectData(void *&ptr, int &size) {
- FlyingObjectShape *d = (FlyingObjectShape *)ptr;
- delete[] d;
- ptr = 0;
- size = 0;
-}
-
-
-void StaticResource::freeRawDataBe16(void *&ptr, int &size) {
- uint16 *data = (uint16 *)ptr;
- delete[] data;
- ptr = 0;
- size = 0;
-}
-
-void StaticResource::freeRawDataBe32(void *&ptr, int &size) {
- uint32 *data = (uint32 *)ptr;
- delete[] data;
- ptr = 0;
- size = 0;
-}
-
-void StaticResource::freeButtonDefs(void *&ptr, int &size) {
- ButtonDef *d = (ButtonDef *)ptr;
- delete[] d;
- ptr = 0;
- size = 0;
-}
-#endif // ENABLE_LOL
-
#pragma mark -
void KyraEngine_LoK::initStaticResource() {
@@ -1360,371 +1121,6 @@ void KyraEngine_MR::initStaticResource() {
_itemStringMap = _staticres->loadRawData(k3ItemStringMap, _itemStringMapSize);
}
-#ifdef ENABLE_LOL
-// TODO: move this to lol.cpp maybe?
-void LoLEngine::initStaticResource() {
- // assign music data
- static const char *pcMusicFileListIntro[] = { "LOREINTR" };
- static const char *pcMusicFileListFinale[] = { "LOREFINL" };
- static const char *pcMusicFileListIngame[] = { "LORE%02d%c" };
-
- static const char *pc98MusicFileListIntro[] = { 0, "lore84.86", "lore82.86", 0, 0, 0, "lore83.86", "lore81.86" };
- static const char *pc98MusicFileListFinale[] = { 0, 0, "lore85.86", "lore86.86", "lore87.86" };
- static const char *pc98MusicFileListIngame[] = { "lore%02d.86" };
-
- memset(_soundData, 0, sizeof(_soundData));
- if (_flags.platform == Common::kPlatformPC) {
- _soundData[0].fileList = pcMusicFileListIntro;
- _soundData[0].fileListLen = ARRAYSIZE(pcMusicFileListIntro);
- _soundData[1].fileList = pcMusicFileListIngame;
- _soundData[1].fileListLen = ARRAYSIZE(pcMusicFileListIngame);
- _soundData[2].fileList = pcMusicFileListFinale;
- _soundData[2].fileListLen = ARRAYSIZE(pcMusicFileListFinale);
- } else if (_flags.platform == Common::kPlatformPC98) {
- _soundData[0].fileList = pc98MusicFileListIntro;
- _soundData[0].fileListLen = ARRAYSIZE(pc98MusicFileListIntro);
- _soundData[1].fileList = pc98MusicFileListIngame;
- _soundData[1].fileListLen = ARRAYSIZE(pc98MusicFileListIngame);
- _soundData[2].fileList = pc98MusicFileListFinale;
- _soundData[2].fileListLen = ARRAYSIZE(pc98MusicFileListFinale);
- }
-
- if (_flags.isDemo)
- return;
-
- _pakFileList = _staticres->loadStrings(kLolIngamePakFiles, _pakFileListSize);
- _charDefaults = _staticres->loadCharData(kLolCharacterDefs, _charDefaultsSize);
- _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLolIngameSfxIndex, _ingameSoundIndexSize);
- _musicTrackMap = _staticres->loadRawData(kLolMusicTrackMap, _musicTrackMapSize);
- _ingameGMSoundIndex = _staticres->loadRawData(kLolIngameGMSfxIndex, _ingameGMSoundIndexSize);
- _ingameMT32SoundIndex = _staticres->loadRawData(kLolIngameMT32SfxIndex, _ingameMT32SoundIndexSize);
- _ingamePCSpeakerSoundIndex = _staticres->loadRawData(kLolIngamePcSpkSfxIndex, _ingamePCSpeakerSoundIndexSize);
- _spellProperties = _staticres->loadSpellData(kLolSpellProperties, _spellPropertiesSize);
- _gameShapeMap = (const int8 *)_staticres->loadRawData(kLolGameShapeMap, _gameShapeMapSize);
- _sceneItemOffs = (const int8 *)_staticres->loadRawData(kLolSceneItemOffs, _sceneItemOffsSize);
- _charInvIndex = _staticres->loadRawData(kLolCharInvIndex, _charInvIndexSize);
- _charInvDefs = _staticres->loadRawData(kLolCharInvDefs, _charInvDefsSize);
- _charDefsMan = _staticres->loadRawDataBe16(kLolCharDefsMan, _charDefsManSize);
- _charDefsWoman = _staticres->loadRawDataBe16(kLolCharDefsWoman, _charDefsWomanSize);
- _charDefsKieran = _staticres->loadRawDataBe16(kLolCharDefsKieran, _charDefsKieranSize);
- _charDefsAkshel = _staticres->loadRawDataBe16(kLolCharDefsAkshel, _charDefsAkshelSize);
- _expRequirements = (const int32 *)_staticres->loadRawDataBe32(kLolExpRequirements, _expRequirementsSize);
- _monsterModifiers = _staticres->loadRawDataBe16(kLolMonsterModifiers, _monsterModifiersSize);
- _monsterShiftOffs = (const int8 *)_staticres->loadRawData(kLolMonsterShiftOffsets, _monsterShiftOffsSize);
- _monsterDirFlags = _staticres->loadRawData(kLolMonsterDirFlags, _monsterDirFlagsSize);
- _monsterScaleX = _staticres->loadRawData(kLolMonsterScaleX, _monsterScaleXSize);
- _monsterScaleY = _staticres->loadRawData(kLolMonsterScaleY, _monsterScaleYSize);
- _monsterScaleWH = _staticres->loadRawDataBe16(kLolMonsterScaleWH, _monsterScaleWHSize);
- _inventorySlotDesc = _staticres->loadRawDataBe16(kLolInventoryDesc, _inventorySlotDescSize);
- _levelShpList = _staticres->loadStrings(kLolLevelShpList, _levelShpListSize);
- _levelDatList = _staticres->loadStrings(kLolLevelDatList, _levelDatListSize);
- _compassDefs = _staticres->loadCompassData(kLolCompassDefs, _compassDefsSize);
- _flyingItemShapes = _staticres->loadFlyingObjectData(kLolFlyingObjectShp, _flyingItemShapesSize);
- _itemCost = _staticres->loadRawDataBe16(kLolItemPrices, _itemCostSize);
- _stashSetupData = _staticres->loadRawData(kLolStashSetup, _stashSetupDataSize);
-
- _dscUnk1 = (const int8 *)_staticres->loadRawData(kLolDscUnk1, _dscUnk1Size);
- _dscShapeIndex = (const int8 *)_staticres->loadRawData(kLolDscShapeIndex, _dscShapeIndexSize);
- _dscOvlMap = _staticres->loadRawData(kLolDscOvlMap, _dscOvlMapSize);
- _dscShapeScaleW = _staticres->loadRawDataBe16(kLolDscScaleWidthData, _dscShapeScaleWSize);
- _dscShapeScaleH = _staticres->loadRawDataBe16(kLolDscScaleHeightData, _dscShapeScaleHSize);
- _dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kLolDscX, _dscShapeXSize);
- _dscShapeY = (const int8 *)_staticres->loadRawData(kLolDscY, _dscShapeYSize);
- _dscTileIndex = _staticres->loadRawData(kLolDscTileIndex, _dscTileIndexSize);
- _dscUnk2 = _staticres->loadRawData(kLolDscUnk2, _dscUnk2Size);
- _dscDoorShpIndex = _staticres->loadRawData(kLolDscDoorShapeIndex, _dscDoorShpIndexSize);
- _dscDim1 = (const int8 *)_staticres->loadRawData(kLolDscDimData1, _dscDim1Size);
- _dscDim2 = (const int8 *)_staticres->loadRawData(kLolDscDimData2, _dscDim2Size);
- _dscBlockMap = _staticres->loadRawData(kLolDscBlockMap, _dscBlockMapSize);
- _dscDimMap = _staticres->loadRawData(kLolDscDimMap, _dscDimMapSize);
- _dscDoorMonsterScaleTable = _staticres->loadRawDataBe16(kLolDscDoorScale, _dscDoorMonsterScaleTableSize);
- _dscShapeOvlIndex = _staticres->loadRawData(kLolDscOvlIndex, _dscShapeOvlIndexSize);
- _dscDoor4 = _staticres->loadRawDataBe16(kLolDscDoor4, _dscDoor4Size);
- _dscBlockIndex = (const int8 *)_staticres->loadRawData(kLolDscBlockIndex, _dscBlockIndexSize);
- _dscDoor1 = _staticres->loadRawData(kLolDscDoor1, _dscDoor1Size);
- _dscDoorMonsterX = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorX, _dscDoorMonsterXSize);
- _dscDoorMonsterY = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorY, _dscDoorMonsterYSize);
-
- _scrollXTop = _staticres->loadRawData(kLolScrollXTop, _scrollXTopSize);
- _scrollYTop = _staticres->loadRawData(kLolScrollYTop, _scrollYTopSize);
- _scrollXBottom = _staticres->loadRawData(kLolScrollXBottom, _scrollXBottomSize);
- _scrollYBottom = _staticres->loadRawData(kLolScrollYBottom, _scrollYBottomSize);
-
- const char *const *tmpSndList = _staticres->loadStrings(kLolIngameSfxFiles, _ingameSoundListSize);
- if (tmpSndList) {
- _ingameSoundList = new char *[_ingameSoundListSize];
- for (int i = 0; i < _ingameSoundListSize; i++) {
- _ingameSoundList[i] = new char[strlen(tmpSndList[i]) + 1];
- strcpy(_ingameSoundList[i], tmpSndList[i]);
- }
- _staticres->unloadId(kLolIngameSfxFiles);
- }
-
- _buttonData = _staticres->loadButtonDefs(kLolButtonDefs, _buttonDataSize);
- _buttonList1 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList1, _buttonList1Size);
- _buttonList2 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList2, _buttonList2Size);
- _buttonList3 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList3, _buttonList3Size);
- _buttonList4 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList4, _buttonList4Size);
- _buttonList5 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList5, _buttonList5Size);
- _buttonList6 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList6, _buttonList6Size);
- _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList7, _buttonList7Size);
- _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList8, _buttonList8Size);
-
- _autoMapStrings = _staticres->loadRawDataBe16(kLolMapStringId, _autoMapStringsSize);
-
- int tmpSize = 0;
- const uint8 *tmp = _staticres->loadRawData(kLolLegendData, tmpSize);
- tmpSize /= 5;
- if (tmp) {
- _defaultLegendData = new MapLegendData[tmpSize];
- for (int i = 0; i < tmpSize; i++) {
- _defaultLegendData[i].shapeIndex = *tmp++;
- _defaultLegendData[i].enable = *tmp++ ? true : false;
- _defaultLegendData[i].x = (int8)*tmp++;
- _defaultLegendData[i].stringId = READ_LE_UINT16(tmp);
- tmp += 2;
- }
- _staticres->unloadId(kLolLegendData);
- }
-
- tmp = _staticres->loadRawData(kLolMapCursorOvl, tmpSize);
- _mapCursorOverlay = new uint8[tmpSize];
- memcpy(_mapCursorOverlay, tmp, tmpSize);
- _staticres->unloadId(kLolMapCursorOvl);
-
- _updateSpellBookCoords = _staticres->loadRawData(kLolSpellbookCoords, _updateSpellBookCoordsSize);
- _updateSpellBookAnimData = _staticres->loadRawData(kLolSpellbookAnim, _updateSpellBookAnimDataSize);
- _healShapeFrames = _staticres->loadRawData(kLolHealShapeFrames, _healShapeFramesSize);
-
- tmp = _staticres->loadRawData(kLolLightningDefs, tmpSize);
- if (tmp) {
- _lightningProps = new LightningProperty[5];
- for (int i = 0; i < 5; i++) {
- _lightningProps[i].lastFrame = tmp[i << 2];
- _lightningProps[i].frameDiv = tmp[(i << 2) + 1];
- _lightningProps[i].sfxId = READ_LE_UINT16(&tmp[(i << 2) + 2]);
- }
- _staticres->unloadId(kLolLightningDefs);
- }
-
- _fireBallCoords = (const int16 *)_staticres->loadRawDataBe16(kLolFireballCoords, _fireBallCoordsSize);
-
- _buttonCallbacks.clear();
- _buttonCallbacks.reserve(95);
-#define cb(x) _buttonCallbacks.push_back(BUTTON_FUNCTOR(LoLEngine, this, &LoLEngine::x))
- // 0x00
- cb(clickedUpArrow);
- cb(clickedDownArrow);
- _buttonCallbacks.push_back(_buttonCallbacks[1]);
- cb(clickedLeftArrow);
-
- // 0x04
- cb(clickedRightArrow);
- cb(clickedTurnLeftArrow);
- cb(clickedTurnRightArrow);
- cb(clickedAttackButton);
-
- // 0x08
- for (int i = 0; i < 3; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[7]);
- cb(clickedMagicButton);
-
- // 0x0C
- for (int i = 0; i < 3; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[11]);
- cb(clickedMagicSubmenu);
-
- // 0x10
- cb(clickedScreen);
- cb(clickedPortraitLeft);
- for (int i = 0; i < 7; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[17]);
-
- // 0x19
- cb(clickedLiveMagicBarsLeft);
- for (int i = 0; i < 3; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[25]);
-
- // 0x1D
- cb(clickedPortraitEtcRight);
- for (int i = 0; i < 3; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[29]);
-
- // 0x21
- cb(clickedCharInventorySlot);
- for (int i = 0; i < 10; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[33]);
-
- // 0x2C
- cb(clickedExitCharInventory);
- cb(clickedSceneDropItem);
- for (int i = 0; i < 3; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[45]);
-
- // 0x31
- cb(clickedScenePickupItem);
- cb(clickedInventorySlot);
- for (int i = 0; i < 9; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[50]);
-
- // 0x3C
- cb(clickedInventoryScroll);
- cb(clickedInventoryScroll);
- cb(clickedWall);
- _buttonCallbacks.push_back(_buttonCallbacks[62]);
-
- // 0x40
- cb(clickedSequenceWindow);
- _buttonCallbacks.push_back(_buttonCallbacks[0]);
- _buttonCallbacks.push_back(_buttonCallbacks[1]);
- _buttonCallbacks.push_back(_buttonCallbacks[3]);
-
- // 0x44
- _buttonCallbacks.push_back(_buttonCallbacks[4]);
- _buttonCallbacks.push_back(_buttonCallbacks[5]);
- _buttonCallbacks.push_back(_buttonCallbacks[6]);
- cb(clickedScroll);
-
- // 0x48
- for (int i = 0; i < 9; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[71]);
-
- // 0x51
- cb(clickedSpellTargetCharacter);
- for (int i = 0; i < 3; ++i)
- _buttonCallbacks.push_back(_buttonCallbacks[81]);
-
- // 0x55
- cb(clickedSpellTargetScene);
- cb(clickedSceneThrowItem);
- _buttonCallbacks.push_back(_buttonCallbacks[86]);
-
- // 0x58
- cb(clickedOptions);
- cb(clickedRestParty);
- cb(clickedMoneyBox);
- cb(clickedCompass);
-
- // 0x5C
- cb(clickedAutomap);
- cb(clickedLamp);
- cb(clickedStatusIcon);
-#undef cb
-}
-
-void GUI_LoL::initStaticData() {
- GUI_V2_BUTTON(_scrollUpButton, 20, 96, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0);
- GUI_V2_BUTTON(_scrollDownButton, 21, 98, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0);
-
- for (uint i = 0; i < ARRAYSIZE(_menuButtons); ++i)
- GUI_V2_BUTTON(_menuButtons[i], i, 0, 0, 0, 0, 0, 0x4487, 0, 0, 0, 0, 0, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0);
-
- if (_vm->gameFlags().isTalkie)
- GUI_LOL_MENU(_mainMenu, 9, 0x4000, 0, 7, -1, -1, -1, -1);
- else
- GUI_LOL_MENU(_mainMenu, 17, 0x4000, 0, 6, -1, -1, -1, -1);
-
- GUI_LOL_MENU_ITEM(_mainMenu.item[0], 0x4001, 16, 23, 176, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_mainMenu.item[1], 0x4002, 16, 40, 176, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_mainMenu.item[2], 0x4003, 16, 57, 176, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_mainMenu.item[3], 0x4004, 16, 74, 176, 15, 0, 0);
-
- if (_vm->gameFlags().isTalkie) {
- GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x42D9, 16, 91, 176, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4006, 16, 108, 176, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_mainMenu.item[6], 0x4005, 88, 127, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- } else {
- GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x4006, 16, 91, 176, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4005, 88, 110, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- }
-
- Button::Callback mainMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedMainMenu);
- for (int i = 0; i < _mainMenu.numberOfItems; ++i)
- _mainMenu.item[i].callback = mainMenuFunctor;
-
- GUI_LOL_MENU(_loadMenu, 10, 0x400e, 1, 5, 128, 20, 128, 118);
- GUI_LOL_MENU_ITEM(_loadMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_loadMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_loadMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_loadMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_loadMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- Button::Callback loadMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedLoadMenu);
- for (int i = 0; i < 5; ++i)
- _loadMenu.item[i].callback = loadMenuFunctor;
-
- GUI_LOL_MENU(_saveMenu, 10, 0x400d, 1, 5, 128, 20, 128, 118);
- GUI_LOL_MENU_ITEM(_saveMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_saveMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_saveMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_saveMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_saveMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- Button::Callback saveMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSaveMenu);
- for (int i = 0; i < 5; ++i)
- _saveMenu.item[i].callback = saveMenuFunctor;
-
- GUI_LOL_MENU(_deleteMenu, 10, 0x400f, 1, 5, 128, 20, 128, 118);
- GUI_LOL_MENU_ITEM(_deleteMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_deleteMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_deleteMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_deleteMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_deleteMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- Button::Callback deleteMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeleteMenu);
- for (int i = 0; i < 5; ++i)
- _deleteMenu.item[i].callback = deleteMenuFunctor;
-
- GUI_LOL_MENU(_gameOptions, 17, 0x400c, 0, 6, -1, -1, -1, -1);
- if (_vm->gameFlags().isTalkie) {
- GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff7, 120, 22, 80, 15, 0x406e, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff6, 120, 39, 80, 15, 0x406c, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff5, 120, 56, 80, 15, 0x406d, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff4, 120, 73, 80, 15, 0x42d5, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff3, 120, 90, 80, 15, 0x42d2, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- } else {
- GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff9, 120, 22, 80, 15, 0x406a, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff8, 120, 39, 80, 15, 0x406b, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff7, 120, 56, 80, 15, 0x406e, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff6, 120, 73, 80, 15, 0x406c, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff5, 120, 90, 80, 15, 0x406d, 0);
- GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- }
- Button::Callback optionsMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedOptionsMenu);
- for (int i = 0; i < _gameOptions.numberOfItems; ++i)
- _gameOptions.item[i].callback = optionsMenuFunctor;
-
- GUI_LOL_MENU(_audioOptions, 18, 0x42d9, 2, 1, -1, -1, -1, -1);
- GUI_LOL_MENU_ITEM(_audioOptions.item[0], 0x4072, 152, 76, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- GUI_LOL_MENU_ITEM(_audioOptions.item[1], 3, 128, 22, 114, 14, 0x42db, 0);
- GUI_LOL_MENU_ITEM(_audioOptions.item[2], 4, 128, 39, 114, 14, 0x42da, 0);
- GUI_LOL_MENU_ITEM(_audioOptions.item[3], 5, 128, 56, 114, 14, 0x42dc, 0);
- Button::Callback audioMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedAudioMenu);
- for (int i = 0; i < 4; ++i)
- _audioOptions.item[i].callback = audioMenuFunctor;
-
- GUI_LOL_MENU(_deathMenu, 11, 0x4013, 0, 2, -1, -1, -1, -1);
- GUI_LOL_MENU_ITEM(_deathMenu.item[0], 0x4006, 8, 30, 104, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_deathMenu.item[1], 0x4001, 176, 30, 104, 15, 0, 0);
- Button::Callback deathMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeathMenu);
- for (int i = 0; i < 2; ++i)
- _deathMenu.item[i].callback = deathMenuFunctor;
-
- GUI_LOL_MENU(_savenameMenu, 7, 0x4053, 0, 2, -1, -1, -1, -1);
- GUI_LOL_MENU_ITEM(_savenameMenu.item[0], 0x4012, 8, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_RETURN]);
- GUI_LOL_MENU_ITEM(_savenameMenu.item[1], 0x4011, 176, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
- Button::Callback savenameMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSavenameMenu);
- for (int i = 0; i < 2; ++i)
- _savenameMenu.item[i].callback = savenameMenuFunctor;
-
- GUI_LOL_MENU(_choiceMenu, 11, 0, 0, 2, -1, -1, -1, -1);
- GUI_LOL_MENU_ITEM(_choiceMenu.item[0], 0x4007, 8, 30, 72, 15, 0, 0);
- GUI_LOL_MENU_ITEM(_choiceMenu.item[1], 0x4008, 208, 30, 72, 15, 0, 0);
- Button::Callback choiceMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedChoiceMenu);
- for (int i = 0; i < 2; ++i)
- _choiceMenu.item[i].callback = choiceMenuFunctor;
-}
-
-#endif // ENABLE_LOL
-
const uint8 Screen_LoK_16::_palette16[48] = {
0x00, 0x00, 0x00, 0x02, 0x07, 0x0B, 0x0C, 0x06, 0x04,
0x0E, 0x09, 0x07, 0x00, 0x06, 0x03, 0x00, 0x0C, 0x07,
@@ -2745,263 +2141,5 @@ const int8 KyraEngine_MR::_albumWSAY[] = {
-1, -2, 2, 2, -6, -6, -6, 0
};
-// lands of lore static res
-
-#ifdef ENABLE_LOL
-const ScreenDim Screen_LoL::_screenDimTable256C[] = {
- { 0x00, 0x00, 0x28, 0xC8, 0xC7, 0xCF, 0x00, 0x00 }, // Taken from Intro
- { 0x08, 0x48, 0x18, 0x38, 0xFE, 0x01, 0x00, 0x00 },
- { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 },
- { 0x0B, 0x7B, 0x1C, 0x12, 0xFE, 0xFC, 0x00, 0x00 },
- { 0x0B, 0x7B, 0x1C, 0x2D, 0xFE, 0xFC, 0x00, 0x00 },
- { 0x55, 0x7B, 0xE9, 0x37, 0xFE, 0xFC, 0x00, 0x00 },
- { 0x0B, 0x8C, 0x10, 0x2B, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (4 entries)
- { 0x04, 0x59, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 },
- { 0x05, 0x6E, 0x1E, 0x0C, 0xFE, 0x01, 0x00, 0x00 },
- { 0x07, 0x19, 0x1A, 0x97, 0x00, 0x00, 0x00, 0x00 }, // Ingame main menu box CD version
- { 0x03, 0x1E, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 },
- { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 },
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 },
- { 0x0D, 0xA2, 0x18, 0x0C, 0xFE, 0x01, 0x00, 0x00 },
- { 0x0F, 0x06, 0x14, 0x6E, 0x01, 0x00, 0x00, 0x00 },
- { 0x1A, 0xBE, 0x0A, 0x07, 0xFE, 0x01, 0x00, 0x00 },
- { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 },
- { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 },
- { 0x0B, 0x8C, 0x10, 0x33, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (5 entries, CD version only)
- { 0x0B, 0x8C, 0x10, 0x23, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (3 entries, floppy version only)
-
- { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits
- { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
- { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
- { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 },
-};
-
-const ScreenDim Screen_LoL::_screenDimTable16C[] = {
- { 0x00, 0x00, 0x28, 0xC8, 0x33, 0x44, 0x00, 0x00 }, // Taken from Intro
- { 0x08, 0x48, 0x18, 0x38, 0x33, 0x44, 0x00, 0x00 },
- { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 },
- { 0x0B, 0x7B, 0x1C, 0x11, 0x33, 0x11, 0x00, 0x00 },
- { 0x0B, 0x7B, 0x1C, 0x2D, 0x33, 0x11, 0x00, 0x00 },
- { 0x55, 0x7B, 0xE9, 0x37, 0x33, 0x11, 0x00, 0x00 },
- { 0x0B, 0x92, 0x10, 0x2A, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (4 entries)
- { 0x04, 0x58, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 },
- { 0x05, 0x6C, 0x1E, 0x0D, 0x33, 0x44, 0x00, 0x00 },
- { 0x07, 0x20, 0x1A, 0x86, 0x00, 0x00, 0x00, 0x00 },
- { 0x03, 0x20, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 },
- { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 },
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 },
- { 0x0D, 0xA2, 0x18, 0x0C, 0x33, 0x44, 0x00, 0x00 },
- { 0x0F, 0x06, 0x14, 0x6E, 0x44, 0x00, 0x00, 0x00 },
- { 0x1A, 0xBE, 0x0A, 0x07, 0x33, 0x44, 0x00, 0x00 },
- { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 },
- { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 },
- { 0x0B, 0x8C, 0x10, 0x33, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (5 entries, not used here)
- { 0x0B, 0x8C, 0x10, 0x23, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (3 entries)
-
- { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits (TODO: Check this!)
- { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
- { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
- { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 },
-};
-
-const int Screen_LoL::_screenDimTableCount = ARRAYSIZE(Screen_LoL::_screenDimTable256C);
-
-const char * const LoLEngine::_languageExt[] = {
- "ENG",
- "FRE",
- "GER"
-};
-
-const LoLEngine::CharacterPrev LoLEngine::_charPreviews[] = {
- { "Ak\'shel", 0x060, 0x7F, { 0x0F, 0x08, 0x05 } },
- { "Michael", 0x09A, 0x7F, { 0x06, 0x0A, 0x0F } },
- { "Kieran", 0x0D4, 0x7F, { 0x08, 0x06, 0x08 } },
- { "Conrad", 0x10F, 0x7F, { 0x0A, 0x0C, 0x0A } }
-};
-
-const uint16 LoLEngine::_charPosXPC98[] = {
- 92, 152, 212, 268
-};
-
-const uint8 LoLEngine::_charNamesPC98[][11] = {
- { 0x83, 0x41, 0x83, 0x4E, 0x83, 0x56, 0x83, 0x46, 0x83, 0x8B, 0x00 },
- { 0x83, 0x7D, 0x83, 0x43, 0x83, 0x50, 0x83, 0x8B, 0x00, 0x00, 0x00 },
- { 0x83, 0x4C, 0x81, 0x5B, 0x83, 0x89, 0x83, 0x93, 0x00, 0x00, 0x00 },
- { 0x83, 0x52, 0x83, 0x93, 0x83, 0x89, 0x83, 0x62, 0x83, 0x68, 0x00 }
-};
-
-const uint8 LoLEngine::_chargenFrameTableTalkie[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04,
- 0x05, 0x04, 0x03, 0x02, 0x01,
- 0x00, 0x00, 0x01, 0x02, 0x03,
- 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
- 0x0E, 0x0F, 0x10, 0x11, 0x12
-};
-
-const uint8 LoLEngine::_chargenFrameTableFloppy[] = {
- 0, 1, 2, 3, 4, 5, 4, 3, 2,
- 1, 0, 0, 1, 2, 3, 4, 5, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15
-};
-
-const uint16 LoLEngine::_selectionPosTable[] = {
- 0x6F, 0x00, 0x8F, 0x00, 0xAF, 0x00, 0xCF, 0x00,
- 0xEF, 0x00, 0x6F, 0x20, 0x8F, 0x20, 0xAF, 0x20,
- 0xCF, 0x20, 0xEF, 0x20, 0x6F, 0x40, 0x8F, 0x40,
- 0xAF, 0x40, 0xCF, 0x40, 0xEF, 0x40, 0x10F, 0x00
-};
-
-const uint8 LoLEngine::_selectionChar1IdxTable[] = {
- 0, 0, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 0, 0, 5, 5, 5,
- 5, 5, 5, 5, 0, 0, 5, 5,
- 5, 5, 5
-};
-
-const uint8 LoLEngine::_selectionChar2IdxTable[] = {
- 1, 1, 6, 6, 1, 1, 6, 6,
- 6, 6, 6, 6, 6, 1, 1, 6,
- 6, 6, 1, 1, 6, 6, 6, 6,
- 6, 6, 6
-};
-
-const uint8 LoLEngine::_selectionChar3IdxTable[] = {
- 2, 2, 7, 7, 7, 7, 2, 2,
- 7, 7, 7, 7, 7, 7, 7, 2,
- 2, 7, 7, 7, 7, 2, 2, 7,
- 7, 7, 7
-};
-
-const uint8 LoLEngine::_selectionChar4IdxTable[] = {
- 3, 3, 8, 8, 8, 8, 3, 3,
- 8, 8, 3, 3, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 3, 3, 8,
- 8, 8, 8
-};
-
-const uint8 LoLEngine::_reminderChar1IdxTable[] = {
- 4, 4, 4, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 5
-};
-
-const uint8 LoLEngine::_reminderChar2IdxTable[] = {
- 9, 9, 9, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6
-};
-
-const uint8 LoLEngine::_reminderChar3IdxTable[] = {
- 0xE, 0xE, 0xE, 0x7, 0x7, 0x7, 0x7, 0x7,
- 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
- 0x7
-};
-
-const uint8 LoLEngine::_reminderChar4IdxTable[] = {
- 0xF, 0xF, 0xF, 0x8, 0x8, 0x8, 0x8, 0x8,
- 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
- 0x8
-};
-
-const uint8 LoLEngine::_selectionAnimIndexTable[] = {
- 0, 5, 1, 6, 2, 7, 3, 8
-};
-
-const uint8 LoLEngine::_charInfoFrameTable[] = {
- 0x0, 0x7, 0x8, 0x9, 0xA, 0xB, 0xA, 0x9,
- 0x8, 0x7, 0x0, 0x0, 0x7, 0x8, 0x9, 0xA,
- 0xB, 0xA, 0x9, 0x8, 0x7, 0x0, 0x0, 0x7,
- 0x8, 0x9, 0xA, 0xB, 0xA, 0x9, 0x8, 0x7
-};
-
-const uint8 LoLEngine::_clock2Timers[] = {
- 0x00, 0x10, 0x11, 0x03, 0x04, 0x50,
- 0x51, 0x52, 0x08, 0x09, 0x0A
-};
-
-const uint8 LoLEngine::_numClock2Timers = ARRAYSIZE(LoLEngine::_clock2Timers);
-
-const int8 LoLEngine::_mapCoords[12][4] = {
- { 0, 7, 0, -5 }, { -5, 0, 6, 0 }, { 7, 5, 7, 1 },
- { 5, 6, 4, 6 }, { 0, 7, 0, -1 }, { -3, 0, 6, 0 },
- { 6, 7, 6, -3 }, { -3, 5, 6, 5 }, { 1, 5, 1, 1 },
- { 3, 1, 3, 1 }, { -1, 6, -1, -8 }, { -7, -1, 5, -1 }
-};
-
-const MistOfDoomAnimData LoLEngine::_mistAnimData[] = {
- { 0, 7, 7, 13, 155 },
- { 0, 16, 16, 17, 155 },
- { 0, 24, 24, 24, 174 },
- { 0, 19, 19, 19, 174 },
- { 0, 16, 16, 17, 175 },
-};
-
-const char * const LoLEngine::_outroShapeFileTable[] = {
- "AMAZON.SHP", "ARCHRSLG.SHP", "AVIANWRM.SHP", "BANDIT.SHP", "BOAR.SHP", "CABAL.SHP",
- "GUARD.SHP", "HAG.SHP", "HORNET.SHP", "HURZELL.SHP", "IRONGRZR.SHP", "KNOWLES.SHP",
- "LIZARD.SHP", "MANTHA.SHP", "MINOTAUR.SHP", "MORIBUND.SHP", "ORC.SHP", "ORCLDR.SHP",
- "PENTROG.SHP", "RATMAN.SHP", "ROCKLING.SHP", "SCAVNGR.SHP", "STARK.SHP",
- "SWAMPCIT.SHP", "SWAMPMON.SHP", "THUG.SHP", "VIPER.SHP", "XEOB.SHP"
-};
-
-const uint8 LoLEngine::_outroFrameTable[] = {
- 0, 0, 0, 0, 0, 1, 2, 3,
- 0, 1, 2, 3, 8, 9, 10, 11,
- 8, 9, 10, 11, 4, 5, 6, 7
-};
-
-const int16 LoLEngine::_outroRightMonsterPos[] = {
- 205, 55, 205, 55, 205, 55, 205, 55,
- 205, 56, 207, 57, 208, 58, 210, 59,
- 213, 60, 216, 61, 220, 61, 225, 61,
- 230, 61, 235, 61, 240, 61, 240, 61,
- 240, 61, 240, 61, 240, 61, 240, 61,
- 240, 61, 265, 61, 290, 61, 315, 61
-};
-
-const int16 LoLEngine::_outroLeftMonsterPos[] = {
- 92, 55, 92, 55, 92, 55, 92, 55,
- 92, 56, 90, 57, 85, 58, 77, 59,
- 67, 60, 57, 61, 47, 61, 35, 61,
- 35, 61, 35, 61, 35, 61, 35, 61,
- 35, 61, 35, 61, 35, 61, 35, 61,
- 35, 61, 10, 61, -20, 61, -45, 61
-};
-
-const int16 LoLEngine::_outroRightDoorPos[] = {
- 200, 41, 200, 29, 200, 17, 200, 5,
- 200, -7, 200, -7, 200, -7, 200, -7,
- 200, 5, 200, 17, 200, 29, 200, 41,
- 200, 41, 200, 41, 200, 41, 200, 41,
- 200, 41, 200, 41, 200, 41, 200, 41,
- 200, 41, 200, 41, 200, 41, 200, 41
-};
-
-const int16 LoLEngine::_outroLeftDoorPos[] = {
- 72, 41, 72, 29, 72, 17, 72, 5,
- 72, -7, 72, -7, 72, -7, 72, -7,
- 72, 5, 72, 17, 72, 29, 72, 41,
- 72, 41, 72, 41, 72, 41, 72, 41,
- 72, 41, 72, 41, 72, 41, 72, 41,
- 72, 41, 72, 41, 72, 41, 72, 41
-};
-
-const int LoLEngine::_outroMonsterScaleTableX[] = {
- 0x050, 0x050, 0x050, 0x050, 0x050, 0x05D, 0x070, 0x085,
- 0x0A0, 0x0C0, 0x0E2, 0x100, 0x100, 0x100, 0x100, 0x100,
- 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100
-};
-
-const int LoLEngine::_outroMonsterScaleTableY[] = {
- 0x04C, 0x04C, 0x04C, 0x04C, 0x04C, 0x059, 0x06B, 0x080,
- 0x099, 0x0B8, 0x0D9, 0x100, 0x100, 0x100, 0x100, 0x100,
- 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100
-};
-
-#endif // ENABLE_LOL
-
} // End of namespace Kyra
diff --git a/engines/kyra/staticres_lol.cpp b/engines/kyra/staticres_lol.cpp
new file mode 100644
index 0000000000..3287ee37d6
--- /dev/null
+++ b/engines/kyra/staticres_lol.cpp
@@ -0,0 +1,883 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/resource.h"
+#include "kyra/lol.h"
+#include "kyra/screen_lol.h"
+#include "kyra/gui_lol.h"
+
+#ifdef ENABLE_LOL
+
+namespace Kyra {
+
+const LoLCharacter *StaticResource::loadCharData(int id, int &entries) {
+ return (const LoLCharacter *)getData(id, kLolCharData, entries);
+}
+
+const SpellProperty *StaticResource::loadSpellData(int id, int &entries) {
+ return (const SpellProperty *)getData(id, kLolSpellData, entries);
+}
+
+const CompassDef *StaticResource::loadCompassData(int id, int &entries) {
+ return (const CompassDef *)getData(id, kLolCompassData, entries);
+}
+
+const FlyingObjectShape *StaticResource::loadFlyingObjectData(int id, int &entries) {
+ return (const FlyingObjectShape *)getData(id, kLolFlightShpData, entries);
+}
+
+const uint16 *StaticResource::loadRawDataBe16(int id, int &entries) {
+ return (const uint16 *)getData(id, kLolRawDataBe16, entries);
+}
+
+const uint32 *StaticResource::loadRawDataBe32(int id, int &entries) {
+ return (const uint32 *)getData(id, kLolRawDataBe32, entries);
+}
+
+const ButtonDef *StaticResource::loadButtonDefs(int id, int &entries) {
+ return (const ButtonDef *)getData(id, kLolButtonData, entries);
+}
+
+bool StaticResource::loadCharData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
+ size = stream.size() / 130;
+ LoLCharacter *charData = new LoLCharacter[size];
+
+ for (int i = 0; i < size; i++) {
+ LoLCharacter *t = &charData[i];
+
+ t->flags = stream.readUint16LE();
+ stream.read(t->name, 11);
+ t->raceClassSex = stream.readByte();
+ t->id = stream.readSint16LE();
+ t->curFaceFrame = stream.readByte();
+ t->tempFaceFrame = stream.readByte();
+ t->screamSfx = stream.readByte();
+ stream.readUint32LE();
+ for (int ii = 0; ii < 8; ii++)
+ t->itemsMight[ii] = stream.readUint16LE();
+ for (int ii = 0; ii < 8; ii++)
+ t->protectionAgainstItems[ii] = stream.readUint16LE();
+ t->itemProtection = stream.readUint16LE();
+ t->hitPointsCur = stream.readSint16LE();
+ t->hitPointsMax = stream.readUint16LE();
+ t->magicPointsCur = stream.readSint16LE();
+ t->magicPointsMax = stream.readUint16LE();
+ t->field_41 = stream.readByte();
+ t->damageSuffered = stream.readUint16LE();
+ t->weaponHit = stream.readUint16LE();
+ t->totalMightModifier = stream.readUint16LE();
+ t->totalProtectionModifier = stream.readUint16LE();
+ t->might = stream.readUint16LE();
+ t->protection = stream.readUint16LE();
+ t->nextAnimUpdateCountdown = stream.readSint16LE();
+ for (int ii = 0; ii < 11; ii++)
+ t->items[ii] = stream.readUint16LE();
+ for (int ii = 0; ii < 3; ii++)
+ t->skillLevels[ii] = stream.readByte();
+ for (int ii = 0; ii < 3; ii++)
+ t->skillModifiers[ii] = stream.readByte();
+ for (int ii = 0; ii < 3; ii++)
+ t->experiencePts[ii] = stream.readUint32LE();
+ for (int ii = 0; ii < 5; ii++)
+ t->characterUpdateEvents[ii] = stream.readByte();
+ for (int ii = 0; ii < 5; ii++)
+ t->characterUpdateDelay[ii] = stream.readByte();
+ };
+
+ ptr = charData;
+ return true;
+}
+
+bool StaticResource::loadSpellData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
+ size = stream.size() / 28;
+ SpellProperty *spellData = new SpellProperty[size];
+
+ for (int i = 0; i < size; i++) {
+ SpellProperty *t = &spellData[i];
+
+ t->spellNameCode = stream.readUint16LE();
+ for (int ii = 0; ii < 4; ii++)
+ t->mpRequired[ii] = stream.readUint16LE();
+ t->field_a = stream.readUint16LE();
+ t->field_c = stream.readUint16LE();
+ for (int ii = 0; ii < 4; ii++)
+ t->hpRequired[ii] = stream.readUint16LE();
+ t->field_16 = stream.readUint16LE();
+ t->field_18 = stream.readUint16LE();
+ t->flags = stream.readUint16LE();
+ };
+
+ ptr = spellData;
+ return true;
+}
+
+bool StaticResource::loadCompassData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
+ size = stream.size() / 4;
+ CompassDef *defs = new CompassDef[size];
+
+ for (int i = 0; i < size; i++) {
+ CompassDef *t = &defs[i];
+ t->shapeIndex = stream.readByte();
+ t->x = stream.readByte();
+ t->y = stream.readByte();
+ t->flags = stream.readByte();
+ };
+
+
+ ptr = defs;
+ return true;
+}
+
+bool StaticResource::loadFlyingObjectData(Common::SeekableReadStream &stream, void *&ptr, int &size) {
+ size = stream.size() / 5;
+ FlyingObjectShape *defs = new FlyingObjectShape[size];
+
+ for (int i = 0; i < size; i++) {
+ FlyingObjectShape *t = &defs[i];
+ t->shapeFront = stream.readByte();
+ t->shapeBack = stream.readByte();
+ t->shapeLeft = stream.readByte();
+ t->drawFlags = stream.readByte();
+ t->flipFlags = stream.readByte();
+ };
+
+ ptr = defs;
+ return true;
+}
+
+bool StaticResource::loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size) {
+ size = stream.size() >> 1;
+
+ uint16 *r = new uint16[size];
+
+ for (int i = 0; i < size; i++)
+ r[i] = stream.readUint16BE();
+
+ ptr = r;
+ return true;
+}
+
+bool StaticResource::loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size) {
+ size = stream.size() >> 2;
+
+ uint32 *r = new uint32[size];
+
+ for (int i = 0; i < size; i++)
+ r[i] = stream.readUint32BE();
+
+ ptr = r;
+ return true;
+}
+
+bool StaticResource::loadButtonDefs(Common::SeekableReadStream &stream, void *&ptr, int &size) {
+ size = stream.size() / 18;
+
+ ButtonDef *r = new ButtonDef[size];
+
+ for (int i = 0; i < size; i++) {
+ r[i].buttonflags = stream.readUint16BE();
+ r[i].keyCode = stream.readUint16BE();
+ r[i].keyCode2 = stream.readUint16BE();
+ r[i].x = stream.readSint16BE();
+ r[i].y = stream.readSint16BE();
+ r[i].w = stream.readUint16BE();
+ r[i].h = stream.readUint16BE();
+ r[i].index = stream.readUint16BE();
+ r[i].screenDim = stream.readUint16BE();
+ }
+
+ ptr = r;
+ return true;
+}
+
+void StaticResource::freeCharData(void *&ptr, int &size) {
+ LoLCharacter *d = (LoLCharacter *)ptr;
+ delete[] d;
+ ptr = 0;
+ size = 0;
+}
+
+void StaticResource::freeSpellData(void *&ptr, int &size) {
+ SpellProperty *d = (SpellProperty *)ptr;
+ delete[] d;
+ ptr = 0;
+ size = 0;
+}
+
+void StaticResource::freeCompassData(void *&ptr, int &size) {
+ CompassDef *d = (CompassDef *)ptr;
+ delete[] d;
+ ptr = 0;
+ size = 0;
+}
+
+void StaticResource::freeFlyingObjectData(void *&ptr, int &size) {
+ FlyingObjectShape *d = (FlyingObjectShape *)ptr;
+ delete[] d;
+ ptr = 0;
+ size = 0;
+}
+
+
+void StaticResource::freeRawDataBe16(void *&ptr, int &size) {
+ uint16 *data = (uint16 *)ptr;
+ delete[] data;
+ ptr = 0;
+ size = 0;
+}
+
+void StaticResource::freeRawDataBe32(void *&ptr, int &size) {
+ uint32 *data = (uint32 *)ptr;
+ delete[] data;
+ ptr = 0;
+ size = 0;
+}
+
+void StaticResource::freeButtonDefs(void *&ptr, int &size) {
+ ButtonDef *d = (ButtonDef *)ptr;
+ delete[] d;
+ ptr = 0;
+ size = 0;
+}
+
+void LoLEngine::initStaticResource() {
+ // assign music data
+ static const char *pcMusicFileListIntro[] = { "LOREINTR" };
+ static const char *pcMusicFileListFinale[] = { "LOREFINL" };
+ static const char *pcMusicFileListIngame[] = { "LORE%02d%c" };
+
+ static const char *pc98MusicFileListIntro[] = { 0, "lore84.86", "lore82.86", 0, 0, 0, "lore83.86", "lore81.86" };
+ static const char *pc98MusicFileListFinale[] = { 0, 0, "lore85.86", "lore86.86", "lore87.86" };
+ static const char *pc98MusicFileListIngame[] = { "lore%02d.86" };
+
+ memset(_soundData, 0, sizeof(_soundData));
+ if (_flags.platform == Common::kPlatformPC) {
+ _soundData[0].fileList = pcMusicFileListIntro;
+ _soundData[0].fileListLen = ARRAYSIZE(pcMusicFileListIntro);
+ _soundData[1].fileList = pcMusicFileListIngame;
+ _soundData[1].fileListLen = ARRAYSIZE(pcMusicFileListIngame);
+ _soundData[2].fileList = pcMusicFileListFinale;
+ _soundData[2].fileListLen = ARRAYSIZE(pcMusicFileListFinale);
+ } else if (_flags.platform == Common::kPlatformPC98) {
+ _soundData[0].fileList = pc98MusicFileListIntro;
+ _soundData[0].fileListLen = ARRAYSIZE(pc98MusicFileListIntro);
+ _soundData[1].fileList = pc98MusicFileListIngame;
+ _soundData[1].fileListLen = ARRAYSIZE(pc98MusicFileListIngame);
+ _soundData[2].fileList = pc98MusicFileListFinale;
+ _soundData[2].fileListLen = ARRAYSIZE(pc98MusicFileListFinale);
+ }
+
+ if (_flags.isDemo)
+ return;
+
+ _pakFileList = _staticres->loadStrings(kLolIngamePakFiles, _pakFileListSize);
+ _charDefaults = _staticres->loadCharData(kLolCharacterDefs, _charDefaultsSize);
+ _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLolIngameSfxIndex, _ingameSoundIndexSize);
+ _musicTrackMap = _staticres->loadRawData(kLolMusicTrackMap, _musicTrackMapSize);
+ _ingameGMSoundIndex = _staticres->loadRawData(kLolIngameGMSfxIndex, _ingameGMSoundIndexSize);
+ _ingameMT32SoundIndex = _staticres->loadRawData(kLolIngameMT32SfxIndex, _ingameMT32SoundIndexSize);
+ _ingamePCSpeakerSoundIndex = _staticres->loadRawData(kLolIngamePcSpkSfxIndex, _ingamePCSpeakerSoundIndexSize);
+ _spellProperties = _staticres->loadSpellData(kLolSpellProperties, _spellPropertiesSize);
+ _gameShapeMap = (const int8 *)_staticres->loadRawData(kLolGameShapeMap, _gameShapeMapSize);
+ _sceneItemOffs = (const int8 *)_staticres->loadRawData(kLolSceneItemOffs, _sceneItemOffsSize);
+ _charInvIndex = _staticres->loadRawData(kLolCharInvIndex, _charInvIndexSize);
+ _charInvDefs = _staticres->loadRawData(kLolCharInvDefs, _charInvDefsSize);
+ _charDefsMan = _staticres->loadRawDataBe16(kLolCharDefsMan, _charDefsManSize);
+ _charDefsWoman = _staticres->loadRawDataBe16(kLolCharDefsWoman, _charDefsWomanSize);
+ _charDefsKieran = _staticres->loadRawDataBe16(kLolCharDefsKieran, _charDefsKieranSize);
+ _charDefsAkshel = _staticres->loadRawDataBe16(kLolCharDefsAkshel, _charDefsAkshelSize);
+ _expRequirements = (const int32 *)_staticres->loadRawDataBe32(kLolExpRequirements, _expRequirementsSize);
+ _monsterModifiers = _staticres->loadRawDataBe16(kLolMonsterModifiers, _monsterModifiersSize);
+ _monsterShiftOffs = (const int8 *)_staticres->loadRawData(kLolMonsterShiftOffsets, _monsterShiftOffsSize);
+ _monsterDirFlags = _staticres->loadRawData(kLolMonsterDirFlags, _monsterDirFlagsSize);
+ _monsterScaleX = _staticres->loadRawData(kLolMonsterScaleX, _monsterScaleXSize);
+ _monsterScaleY = _staticres->loadRawData(kLolMonsterScaleY, _monsterScaleYSize);
+ _monsterScaleWH = _staticres->loadRawDataBe16(kLolMonsterScaleWH, _monsterScaleWHSize);
+ _inventorySlotDesc = _staticres->loadRawDataBe16(kLolInventoryDesc, _inventorySlotDescSize);
+ _levelShpList = _staticres->loadStrings(kLolLevelShpList, _levelShpListSize);
+ _levelDatList = _staticres->loadStrings(kLolLevelDatList, _levelDatListSize);
+ _compassDefs = _staticres->loadCompassData(kLolCompassDefs, _compassDefsSize);
+ _flyingItemShapes = _staticres->loadFlyingObjectData(kLolFlyingObjectShp, _flyingItemShapesSize);
+ _itemCost = _staticres->loadRawDataBe16(kLolItemPrices, _itemCostSize);
+ _stashSetupData = _staticres->loadRawData(kLolStashSetup, _stashSetupDataSize);
+
+ _dscUnk1 = (const int8 *)_staticres->loadRawData(kLolDscUnk1, _dscUnk1Size);
+ _dscShapeIndex = (const int8 *)_staticres->loadRawData(kLolDscShapeIndex, _dscShapeIndexSize);
+ _dscOvlMap = _staticres->loadRawData(kLolDscOvlMap, _dscOvlMapSize);
+ _dscShapeScaleW = _staticres->loadRawDataBe16(kLolDscScaleWidthData, _dscShapeScaleWSize);
+ _dscShapeScaleH = _staticres->loadRawDataBe16(kLolDscScaleHeightData, _dscShapeScaleHSize);
+ _dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kLolDscX, _dscShapeXSize);
+ _dscShapeY = (const int8 *)_staticres->loadRawData(kLolDscY, _dscShapeYSize);
+ _dscTileIndex = _staticres->loadRawData(kLolDscTileIndex, _dscTileIndexSize);
+ _dscUnk2 = _staticres->loadRawData(kLolDscUnk2, _dscUnk2Size);
+ _dscDoorShpIndex = _staticres->loadRawData(kLolDscDoorShapeIndex, _dscDoorShpIndexSize);
+ _dscDim1 = (const int8 *)_staticres->loadRawData(kLolDscDimData1, _dscDim1Size);
+ _dscDim2 = (const int8 *)_staticres->loadRawData(kLolDscDimData2, _dscDim2Size);
+ _dscBlockMap = _staticres->loadRawData(kLolDscBlockMap, _dscBlockMapSize);
+ _dscDimMap = _staticres->loadRawData(kLolDscDimMap, _dscDimMapSize);
+ _dscDoorMonsterScaleTable = _staticres->loadRawDataBe16(kLolDscDoorScale, _dscDoorMonsterScaleTableSize);
+ _dscShapeOvlIndex = _staticres->loadRawData(kLolDscOvlIndex, _dscShapeOvlIndexSize);
+ _dscDoor4 = _staticres->loadRawDataBe16(kLolDscDoor4, _dscDoor4Size);
+ _dscBlockIndex = (const int8 *)_staticres->loadRawData(kLolDscBlockIndex, _dscBlockIndexSize);
+ _dscDoor1 = _staticres->loadRawData(kLolDscDoor1, _dscDoor1Size);
+ _dscDoorMonsterX = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorX, _dscDoorMonsterXSize);
+ _dscDoorMonsterY = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorY, _dscDoorMonsterYSize);
+
+ _scrollXTop = _staticres->loadRawData(kLolScrollXTop, _scrollXTopSize);
+ _scrollYTop = _staticres->loadRawData(kLolScrollYTop, _scrollYTopSize);
+ _scrollXBottom = _staticres->loadRawData(kLolScrollXBottom, _scrollXBottomSize);
+ _scrollYBottom = _staticres->loadRawData(kLolScrollYBottom, _scrollYBottomSize);
+
+ const char *const *tmpSndList = _staticres->loadStrings(kLolIngameSfxFiles, _ingameSoundListSize);
+ if (tmpSndList) {
+ _ingameSoundList = new char *[_ingameSoundListSize];
+ for (int i = 0; i < _ingameSoundListSize; i++) {
+ _ingameSoundList[i] = new char[strlen(tmpSndList[i]) + 1];
+ strcpy(_ingameSoundList[i], tmpSndList[i]);
+ }
+ _staticres->unloadId(kLolIngameSfxFiles);
+ }
+
+ _buttonData = _staticres->loadButtonDefs(kLolButtonDefs, _buttonDataSize);
+ _buttonList1 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList1, _buttonList1Size);
+ _buttonList2 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList2, _buttonList2Size);
+ _buttonList3 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList3, _buttonList3Size);
+ _buttonList4 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList4, _buttonList4Size);
+ _buttonList5 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList5, _buttonList5Size);
+ _buttonList6 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList6, _buttonList6Size);
+ _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList7, _buttonList7Size);
+ _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList8, _buttonList8Size);
+
+ _autoMapStrings = _staticres->loadRawDataBe16(kLolMapStringId, _autoMapStringsSize);
+
+ int tempSize;
+ const uint8 *tmp = _staticres->loadRawData(kLolLegendData, tempSize);
+ uint8 entrySize = tempSize / 12;
+ tempSize /= entrySize;
+ if (tempSize) {
+ _defaultLegendData = new MapLegendData[tempSize];
+ for (int i = 0; i < tempSize; i++) {
+ _defaultLegendData[i].shapeIndex = *tmp++;
+ _defaultLegendData[i].enable = *tmp++ ? true : false;
+ _defaultLegendData[i].y = (entrySize == 5) ? (int8)*tmp++ : (i == 10 ? -5 : 0);
+ _defaultLegendData[i].stringId = READ_LE_UINT16(tmp);
+ tmp += 2;
+ }
+ _staticres->unloadId(kLolLegendData);
+ }
+
+ tmp = _staticres->loadRawData(kLolMapCursorOvl, tempSize);
+ _mapCursorOverlay = new uint8[tempSize];
+ memcpy(_mapCursorOverlay, tmp, tempSize);
+ _staticres->unloadId(kLolMapCursorOvl);
+
+ _updateSpellBookCoords = _staticres->loadRawData(kLolSpellbookCoords, _updateSpellBookCoordsSize);
+ _updateSpellBookAnimData = _staticres->loadRawData(kLolSpellbookAnim, _updateSpellBookAnimDataSize);
+ _healShapeFrames = _staticres->loadRawData(kLolHealShapeFrames, _healShapeFramesSize);
+
+ tmp = _staticres->loadRawData(kLolLightningDefs, tempSize);
+ if (tmp) {
+ _lightningProps = new LightningProperty[5];
+ for (int i = 0; i < 5; i++) {
+ _lightningProps[i].lastFrame = tmp[i << 2];
+ _lightningProps[i].frameDiv = tmp[(i << 2) + 1];
+ _lightningProps[i].sfxId = READ_LE_UINT16(&tmp[(i << 2) + 2]);
+ }
+ _staticres->unloadId(kLolLightningDefs);
+ }
+
+ _fireBallCoords = (const int16 *)_staticres->loadRawDataBe16(kLolFireballCoords, _fireBallCoordsSize);
+
+ _buttonCallbacks.clear();
+ _buttonCallbacks.reserve(95);
+#define cb(x) _buttonCallbacks.push_back(BUTTON_FUNCTOR(LoLEngine, this, &LoLEngine::x))
+ // 0x00
+ cb(clickedUpArrow);
+ cb(clickedDownArrow);
+ _buttonCallbacks.push_back(_buttonCallbacks[1]);
+ cb(clickedLeftArrow);
+
+ // 0x04
+ cb(clickedRightArrow);
+ cb(clickedTurnLeftArrow);
+ cb(clickedTurnRightArrow);
+ cb(clickedAttackButton);
+
+ // 0x08
+ for (int i = 0; i < 3; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[7]);
+ cb(clickedMagicButton);
+
+ // 0x0C
+ for (int i = 0; i < 3; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[11]);
+ cb(clickedMagicSubmenu);
+
+ // 0x10
+ cb(clickedScreen);
+ cb(clickedPortraitLeft);
+ for (int i = 0; i < 7; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[17]);
+
+ // 0x19
+ cb(clickedLiveMagicBarsLeft);
+ for (int i = 0; i < 3; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[25]);
+
+ // 0x1D
+ cb(clickedPortraitEtcRight);
+ for (int i = 0; i < 3; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[29]);
+
+ // 0x21
+ cb(clickedCharInventorySlot);
+ for (int i = 0; i < 10; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[33]);
+
+ // 0x2C
+ cb(clickedExitCharInventory);
+ cb(clickedSceneDropItem);
+ for (int i = 0; i < 3; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[45]);
+
+ // 0x31
+ cb(clickedScenePickupItem);
+ cb(clickedInventorySlot);
+ for (int i = 0; i < 9; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[50]);
+
+ // 0x3C
+ cb(clickedInventoryScroll);
+ cb(clickedInventoryScroll);
+ cb(clickedWall);
+ _buttonCallbacks.push_back(_buttonCallbacks[62]);
+
+ // 0x40
+ cb(clickedSequenceWindow);
+ _buttonCallbacks.push_back(_buttonCallbacks[0]);
+ _buttonCallbacks.push_back(_buttonCallbacks[1]);
+ _buttonCallbacks.push_back(_buttonCallbacks[3]);
+
+ // 0x44
+ _buttonCallbacks.push_back(_buttonCallbacks[4]);
+ _buttonCallbacks.push_back(_buttonCallbacks[5]);
+ _buttonCallbacks.push_back(_buttonCallbacks[6]);
+ cb(clickedScroll);
+
+ // 0x48
+ for (int i = 0; i < 9; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[71]);
+
+ // 0x51
+ cb(clickedSpellTargetCharacter);
+ for (int i = 0; i < 3; ++i)
+ _buttonCallbacks.push_back(_buttonCallbacks[81]);
+
+ // 0x55
+ cb(clickedSpellTargetScene);
+ cb(clickedSceneThrowItem);
+ _buttonCallbacks.push_back(_buttonCallbacks[86]);
+
+ // 0x58
+ cb(clickedOptions);
+ cb(clickedRestParty);
+ cb(clickedMoneyBox);
+ cb(clickedCompass);
+
+ // 0x5C
+ cb(clickedAutomap);
+ cb(clickedLamp);
+ cb(clickedStatusIcon);
+#undef cb
+}
+
+void GUI_LoL::initStaticData() {
+ GUI_V2_BUTTON(_scrollUpButton, 20, 96, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0);
+ GUI_V2_BUTTON(_scrollDownButton, 21, 98, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0);
+
+ for (uint i = 0; i < ARRAYSIZE(_menuButtons); ++i)
+ GUI_V2_BUTTON(_menuButtons[i], i, 0, 0, 0, 0, 0, 0x4487, 0, 0, 0, 0, 0, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0);
+
+ if (_vm->gameFlags().isTalkie)
+ GUI_LOL_MENU(_mainMenu, 9, 0x4000, 0, 7, -1, -1, -1, -1);
+ else
+ GUI_LOL_MENU(_mainMenu, 17, 0x4000, 0, 6, -1, -1, -1, -1);
+
+ GUI_LOL_MENU_ITEM(_mainMenu.item[0], 0x4001, 16, 23, 176, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_mainMenu.item[1], 0x4002, 16, 40, 176, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_mainMenu.item[2], 0x4003, 16, 57, 176, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_mainMenu.item[3], 0x4004, 16, 74, 176, 15, 0, 0);
+
+ if (_vm->gameFlags().isTalkie) {
+ GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x42D9, 16, 91, 176, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4006, 16, 108, 176, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_mainMenu.item[6], 0x4005, 88, 127, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ } else {
+ GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x4006, 16, 91, 176, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4005, 88, 110, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ }
+
+ Button::Callback mainMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedMainMenu);
+ for (int i = 0; i < _mainMenu.numberOfItems; ++i)
+ _mainMenu.item[i].callback = mainMenuFunctor;
+
+ GUI_LOL_MENU(_loadMenu, 10, 0x400e, 1, 5, 128, 20, 128, 118);
+ GUI_LOL_MENU_ITEM(_loadMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_loadMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_loadMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_loadMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_loadMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ Button::Callback loadMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedLoadMenu);
+ for (int i = 0; i < 5; ++i)
+ _loadMenu.item[i].callback = loadMenuFunctor;
+
+ GUI_LOL_MENU(_saveMenu, 10, 0x400d, 1, 5, 128, 20, 128, 118);
+ GUI_LOL_MENU_ITEM(_saveMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_saveMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_saveMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_saveMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_saveMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ Button::Callback saveMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSaveMenu);
+ for (int i = 0; i < 5; ++i)
+ _saveMenu.item[i].callback = saveMenuFunctor;
+
+ GUI_LOL_MENU(_deleteMenu, 10, 0x400f, 1, 5, 128, 20, 128, 118);
+ GUI_LOL_MENU_ITEM(_deleteMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_deleteMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_deleteMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_deleteMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_deleteMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ Button::Callback deleteMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeleteMenu);
+ for (int i = 0; i < 5; ++i)
+ _deleteMenu.item[i].callback = deleteMenuFunctor;
+
+ GUI_LOL_MENU(_gameOptions, 17, 0x400c, 0, 6, -1, -1, -1, -1);
+ if (_vm->gameFlags().isTalkie) {
+ GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff7, 120, 22, 80, 15, 0x406e, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff6, 120, 39, 80, 15, 0x406c, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff5, 120, 56, 80, 15, 0x406d, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff4, 120, 73, 80, 15, 0x42d5, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff3, 120, 90, 80, 15, 0x42d2, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ } else {
+ GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff9, 120, 22, 80, 15, 0x406a, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff8, 120, 39, 80, 15, 0x406b, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff7, 120, 56, 80, 15, 0x406e, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff6, 120, 73, 80, 15, 0x406c, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff5, 120, 90, 80, 15, 0x406d, 0);
+ GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ }
+ Button::Callback optionsMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedOptionsMenu);
+ for (int i = 0; i < _gameOptions.numberOfItems; ++i)
+ _gameOptions.item[i].callback = optionsMenuFunctor;
+
+ GUI_LOL_MENU(_audioOptions, 18, 0x42d9, 2, 1, -1, -1, -1, -1);
+ GUI_LOL_MENU_ITEM(_audioOptions.item[0], 0x4072, 152, 76, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ GUI_LOL_MENU_ITEM(_audioOptions.item[1], 3, 128, 22, 114, 14, 0x42db, 0);
+ GUI_LOL_MENU_ITEM(_audioOptions.item[2], 4, 128, 39, 114, 14, 0x42da, 0);
+ GUI_LOL_MENU_ITEM(_audioOptions.item[3], 5, 128, 56, 114, 14, 0x42dc, 0);
+ Button::Callback audioMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedAudioMenu);
+ for (int i = 0; i < 4; ++i)
+ _audioOptions.item[i].callback = audioMenuFunctor;
+
+ GUI_LOL_MENU(_deathMenu, 11, 0x4013, 0, 2, -1, -1, -1, -1);
+ GUI_LOL_MENU_ITEM(_deathMenu.item[0], 0x4006, 8, 30, 104, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_deathMenu.item[1], 0x4001, 176, 30, 104, 15, 0, 0);
+ Button::Callback deathMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeathMenu);
+ for (int i = 0; i < 2; ++i)
+ _deathMenu.item[i].callback = deathMenuFunctor;
+
+ GUI_LOL_MENU(_savenameMenu, 7, 0x4053, 0, 2, -1, -1, -1, -1);
+ GUI_LOL_MENU_ITEM(_savenameMenu.item[0], 0x4012, 8, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_RETURN]);
+ GUI_LOL_MENU_ITEM(_savenameMenu.item[1], 0x4011, 176, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]);
+ Button::Callback savenameMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSavenameMenu);
+ for (int i = 0; i < 2; ++i)
+ _savenameMenu.item[i].callback = savenameMenuFunctor;
+
+ GUI_LOL_MENU(_choiceMenu, 11, 0, 0, 2, -1, -1, -1, -1);
+ GUI_LOL_MENU_ITEM(_choiceMenu.item[0], 0x4007, 8, 30, 72, 15, 0, 0);
+ GUI_LOL_MENU_ITEM(_choiceMenu.item[1], 0x4008, 208, 30, 72, 15, 0, 0);
+ Button::Callback choiceMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedChoiceMenu);
+ for (int i = 0; i < 2; ++i)
+ _choiceMenu.item[i].callback = choiceMenuFunctor;
+}
+
+const ScreenDim Screen_LoL::_screenDimTable256C[] = {
+ { 0x00, 0x00, 0x28, 0xC8, 0xC7, 0xCF, 0x00, 0x00 }, // Taken from Intro
+ { 0x08, 0x48, 0x18, 0x38, 0xFE, 0x01, 0x00, 0x00 },
+ { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 },
+ { 0x0B, 0x7B, 0x1C, 0x12, 0xFE, 0xFC, 0x00, 0x00 },
+ { 0x0B, 0x7B, 0x1C, 0x2D, 0xFE, 0xFC, 0x00, 0x00 },
+ { 0x55, 0x7B, 0xE9, 0x37, 0xFE, 0xFC, 0x00, 0x00 },
+ { 0x0B, 0x8C, 0x10, 0x2B, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (4 entries)
+ { 0x04, 0x59, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x05, 0x6E, 0x1E, 0x0C, 0xFE, 0x01, 0x00, 0x00 },
+ { 0x07, 0x19, 0x1A, 0x97, 0x00, 0x00, 0x00, 0x00 }, // Ingame main menu box CD version
+ { 0x03, 0x1E, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 },
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 },
+ { 0x0D, 0xA2, 0x18, 0x0C, 0xFE, 0x01, 0x00, 0x00 },
+ { 0x0F, 0x06, 0x14, 0x6E, 0x01, 0x00, 0x00, 0x00 },
+ { 0x1A, 0xBE, 0x0A, 0x07, 0xFE, 0x01, 0x00, 0x00 },
+ { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 },
+ { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 },
+ { 0x0B, 0x8C, 0x10, 0x33, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (5 entries, CD version only)
+ { 0x0B, 0x8C, 0x10, 0x23, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (3 entries, floppy version only)
+
+ { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits
+ { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 },
+};
+
+const ScreenDim Screen_LoL::_screenDimTable16C[] = {
+ { 0x00, 0x00, 0x28, 0xC8, 0x33, 0x44, 0x00, 0x00 }, // Taken from Intro
+ { 0x08, 0x48, 0x18, 0x38, 0x33, 0x44, 0x00, 0x00 },
+ { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 },
+ { 0x0B, 0x7B, 0x1C, 0x11, 0x33, 0x11, 0x00, 0x00 },
+ { 0x0B, 0x7B, 0x1C, 0x2D, 0x33, 0x11, 0x00, 0x00 },
+ { 0x55, 0x7B, 0xE9, 0x37, 0x33, 0x11, 0x00, 0x00 },
+ { 0x0B, 0x92, 0x10, 0x2A, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (4 entries)
+ { 0x04, 0x58, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x05, 0x6C, 0x1E, 0x0D, 0x33, 0x44, 0x00, 0x00 },
+ { 0x07, 0x20, 0x1A, 0x86, 0x00, 0x00, 0x00, 0x00 },
+ { 0x03, 0x20, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 },
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 },
+ { 0x0D, 0xA2, 0x18, 0x0C, 0x33, 0x44, 0x00, 0x00 },
+ { 0x0F, 0x06, 0x14, 0x6E, 0x44, 0x00, 0x00, 0x00 },
+ { 0x1A, 0xBE, 0x0A, 0x07, 0x33, 0x44, 0x00, 0x00 },
+ { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 },
+ { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 },
+ { 0x0B, 0x8C, 0x10, 0x33, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (5 entries, not used here)
+ { 0x0B, 0x8C, 0x10, 0x23, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (3 entries)
+
+ { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits (TODO: Check this!)
+ { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 },
+ { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 },
+};
+
+const int Screen_LoL::_screenDimTableCount = ARRAYSIZE(Screen_LoL::_screenDimTable256C);
+
+const char * const LoLEngine::_languageExt[] = {
+ "ENG",
+ "FRE",
+ "GER"
+};
+
+const LoLEngine::CharacterPrev LoLEngine::_charPreviews[] = {
+ { "Ak\'shel", 0x060, 0x7F, { 0x0F, 0x08, 0x05 } },
+ { "Michael", 0x09A, 0x7F, { 0x06, 0x0A, 0x0F } },
+ { "Kieran", 0x0D4, 0x7F, { 0x08, 0x06, 0x08 } },
+ { "Conrad", 0x10F, 0x7F, { 0x0A, 0x0C, 0x0A } }
+};
+
+const uint16 LoLEngine::_charPosXPC98[] = {
+ 92, 152, 212, 268
+};
+
+const uint8 LoLEngine::_charNamesPC98[][11] = {
+ { 0x83, 0x41, 0x83, 0x4E, 0x83, 0x56, 0x83, 0x46, 0x83, 0x8B, 0x00 },
+ { 0x83, 0x7D, 0x83, 0x43, 0x83, 0x50, 0x83, 0x8B, 0x00, 0x00, 0x00 },
+ { 0x83, 0x4C, 0x81, 0x5B, 0x83, 0x89, 0x83, 0x93, 0x00, 0x00, 0x00 },
+ { 0x83, 0x52, 0x83, 0x93, 0x83, 0x89, 0x83, 0x62, 0x83, 0x68, 0x00 }
+};
+
+const uint8 LoLEngine::_chargenFrameTableTalkie[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x04, 0x03, 0x02, 0x01,
+ 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x0F, 0x10, 0x11, 0x12
+};
+
+const uint8 LoLEngine::_chargenFrameTableFloppy[] = {
+ 0, 1, 2, 3, 4, 5, 4, 3, 2,
+ 1, 0, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+const uint16 LoLEngine::_selectionPosTable[] = {
+ 0x6F, 0x00, 0x8F, 0x00, 0xAF, 0x00, 0xCF, 0x00,
+ 0xEF, 0x00, 0x6F, 0x20, 0x8F, 0x20, 0xAF, 0x20,
+ 0xCF, 0x20, 0xEF, 0x20, 0x6F, 0x40, 0x8F, 0x40,
+ 0xAF, 0x40, 0xCF, 0x40, 0xEF, 0x40, 0x10F, 0x00
+};
+
+const uint8 LoLEngine::_selectionChar1IdxTable[] = {
+ 0, 0, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 0, 0, 5, 5, 5,
+ 5, 5, 5, 5, 0, 0, 5, 5,
+ 5, 5, 5
+};
+
+const uint8 LoLEngine::_selectionChar2IdxTable[] = {
+ 1, 1, 6, 6, 1, 1, 6, 6,
+ 6, 6, 6, 6, 6, 1, 1, 6,
+ 6, 6, 1, 1, 6, 6, 6, 6,
+ 6, 6, 6
+};
+
+const uint8 LoLEngine::_selectionChar3IdxTable[] = {
+ 2, 2, 7, 7, 7, 7, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2,
+ 2, 7, 7, 7, 7, 2, 2, 7,
+ 7, 7, 7
+};
+
+const uint8 LoLEngine::_selectionChar4IdxTable[] = {
+ 3, 3, 8, 8, 8, 8, 3, 3,
+ 8, 8, 3, 3, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 3, 3, 8,
+ 8, 8, 8
+};
+
+const uint8 LoLEngine::_reminderChar1IdxTable[] = {
+ 4, 4, 4, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5
+};
+
+const uint8 LoLEngine::_reminderChar2IdxTable[] = {
+ 9, 9, 9, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6
+};
+
+const uint8 LoLEngine::_reminderChar3IdxTable[] = {
+ 0xE, 0xE, 0xE, 0x7, 0x7, 0x7, 0x7, 0x7,
+ 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
+ 0x7
+};
+
+const uint8 LoLEngine::_reminderChar4IdxTable[] = {
+ 0xF, 0xF, 0xF, 0x8, 0x8, 0x8, 0x8, 0x8,
+ 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
+ 0x8
+};
+
+const uint8 LoLEngine::_selectionAnimIndexTable[] = {
+ 0, 5, 1, 6, 2, 7, 3, 8
+};
+
+const uint8 LoLEngine::_charInfoFrameTable[] = {
+ 0x0, 0x7, 0x8, 0x9, 0xA, 0xB, 0xA, 0x9,
+ 0x8, 0x7, 0x0, 0x0, 0x7, 0x8, 0x9, 0xA,
+ 0xB, 0xA, 0x9, 0x8, 0x7, 0x0, 0x0, 0x7,
+ 0x8, 0x9, 0xA, 0xB, 0xA, 0x9, 0x8, 0x7
+};
+
+const uint8 LoLEngine::_clock2Timers[] = {
+ 0x00, 0x10, 0x11, 0x03, 0x04, 0x50,
+ 0x51, 0x52, 0x08, 0x09, 0x0A
+};
+
+const uint8 LoLEngine::_numClock2Timers = ARRAYSIZE(LoLEngine::_clock2Timers);
+
+const int8 LoLEngine::_mapCoords[12][4] = {
+ { 0, 7, 0, -5 }, { -5, 0, 6, 0 }, { 7, 5, 7, 1 },
+ { 5, 6, 4, 6 }, { 0, 7, 0, -1 }, { -3, 0, 6, 0 },
+ { 6, 7, 6, -3 }, { -3, 5, 6, 5 }, { 1, 5, 1, 1 },
+ { 3, 1, 3, 1 }, { -1, 6, -1, -8 }, { -7, -1, 5, -1 }
+};
+
+const MistOfDoomAnimData LoLEngine::_mistAnimData[] = {
+ { 0, 7, 7, 13, 155 },
+ { 0, 16, 16, 17, 155 },
+ { 0, 24, 24, 24, 174 },
+ { 0, 19, 19, 19, 174 },
+ { 0, 16, 16, 17, 175 },
+};
+
+const char * const LoLEngine::_outroShapeFileTable[] = {
+ "AMAZON.SHP", "ARCHRSLG.SHP", "AVIANWRM.SHP", "BANDIT.SHP", "BOAR.SHP", "CABAL.SHP",
+ "GUARD.SHP", "HAG.SHP", "HORNET.SHP", "HURZELL.SHP", "IRONGRZR.SHP", "KNOWLES.SHP",
+ "LIZARD.SHP", "MANTHA.SHP", "MINOTAUR.SHP", "MORIBUND.SHP", "ORC.SHP", "ORCLDR.SHP",
+ "PENTROG.SHP", "RATMAN.SHP", "ROCKLING.SHP", "SCAVNGR.SHP", "STARK.SHP",
+ "SWAMPCIT.SHP", "SWAMPMON.SHP", "THUG.SHP", "VIPER.SHP", "XEOB.SHP"
+};
+
+const uint8 LoLEngine::_outroFrameTable[] = {
+ 0, 0, 0, 0, 0, 1, 2, 3,
+ 0, 1, 2, 3, 8, 9, 10, 11,
+ 8, 9, 10, 11, 4, 5, 6, 7
+};
+
+const int16 LoLEngine::_outroRightMonsterPos[] = {
+ 205, 55, 205, 55, 205, 55, 205, 55,
+ 205, 56, 207, 57, 208, 58, 210, 59,
+ 213, 60, 216, 61, 220, 61, 225, 61,
+ 230, 61, 235, 61, 240, 61, 240, 61,
+ 240, 61, 240, 61, 240, 61, 240, 61,
+ 240, 61, 265, 61, 290, 61, 315, 61
+};
+
+const int16 LoLEngine::_outroLeftMonsterPos[] = {
+ 92, 55, 92, 55, 92, 55, 92, 55,
+ 92, 56, 90, 57, 85, 58, 77, 59,
+ 67, 60, 57, 61, 47, 61, 35, 61,
+ 35, 61, 35, 61, 35, 61, 35, 61,
+ 35, 61, 35, 61, 35, 61, 35, 61,
+ 35, 61, 10, 61, -20, 61, -45, 61
+};
+
+const int16 LoLEngine::_outroRightDoorPos[] = {
+ 200, 41, 200, 29, 200, 17, 200, 5,
+ 200, -7, 200, -7, 200, -7, 200, -7,
+ 200, 5, 200, 17, 200, 29, 200, 41,
+ 200, 41, 200, 41, 200, 41, 200, 41,
+ 200, 41, 200, 41, 200, 41, 200, 41,
+ 200, 41, 200, 41, 200, 41, 200, 41
+};
+
+const int16 LoLEngine::_outroLeftDoorPos[] = {
+ 72, 41, 72, 29, 72, 17, 72, 5,
+ 72, -7, 72, -7, 72, -7, 72, -7,
+ 72, 5, 72, 17, 72, 29, 72, 41,
+ 72, 41, 72, 41, 72, 41, 72, 41,
+ 72, 41, 72, 41, 72, 41, 72, 41,
+ 72, 41, 72, 41, 72, 41, 72, 41
+};
+
+const int LoLEngine::_outroMonsterScaleTableX[] = {
+ 0x050, 0x050, 0x050, 0x050, 0x050, 0x05D, 0x070, 0x085,
+ 0x0A0, 0x0C0, 0x0E2, 0x100, 0x100, 0x100, 0x100, 0x100,
+ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100
+};
+
+const int LoLEngine::_outroMonsterScaleTableY[] = {
+ 0x04C, 0x04C, 0x04C, 0x04C, 0x04C, 0x059, 0x06B, 0x080,
+ 0x099, 0x0B8, 0x0D9, 0x100, 0x100, 0x100, 0x100, 0x100,
+ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100
+};
+
+} // End of namespace Kyra
+
+#endif
+
diff --git a/engines/kyra/text_hof.cpp b/engines/kyra/text_hof.cpp
index 48eda97f80..9d20cdd51a 100644
--- a/engines/kyra/text_hof.cpp
+++ b/engines/kyra/text_hof.cpp
@@ -255,7 +255,8 @@ void KyraEngine_HoF::objectChatInit(const char *str, int object, int vocHigh, in
_screen->hideMouse();
- if (textEnabled()) {
+ _chatTextEnabled = textEnabled();
+ if (_chatTextEnabled) {
objectChatPrintText(str, object);
_chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength;
} else {
diff --git a/engines/kyra/text_lok.cpp b/engines/kyra/text_lok.cpp
index 3e45c0f286..40f2217a2b 100644
--- a/engines/kyra/text_lok.cpp
+++ b/engines/kyra/text_lok.cpp
@@ -32,10 +32,9 @@
namespace Kyra {
-void KyraEngine_LoK::waitForChatToFinish(int vocFile, int16 chatDuration, const char *chatStr, uint8 charNum) {
+void KyraEngine_LoK::waitForChatToFinish(int vocFile, int16 chatDuration, const char *chatStr, uint8 charNum, const bool printText) {
bool hasUpdatedNPCs = false;
bool runLoop = true;
- bool drawText = textEnabled();
uint8 currPage;
uint32 timeToEnd = strlen(chatStr) * 8 * _tickLength + _system->getMillis();
@@ -92,7 +91,7 @@ void KyraEngine_LoK::waitForChatToFinish(int vocFile, int16 chatDuration, const
_animator->preserveAnyChangedBackgrounds();
_animator->prepDrawAllObjects();
- if (drawText) {
+ if (printText) {
currPage = _screen->_curPage;
_screen->_curPage = 2;
_text->printCharacterText(chatStr, charNum, _characterList[charNum].x1);
@@ -102,7 +101,7 @@ void KyraEngine_LoK::waitForChatToFinish(int vocFile, int16 chatDuration, const
_animator->copyChangedObjectsForward(0);
updateTextFade();
- if (((chatDuration < (int16)(_system->getMillis() - timeAtStart)) && chatDuration != -1 && drawText) || (!drawText && !snd_voiceIsPlaying()))
+ if (((chatDuration < (int16)(_system->getMillis() - timeAtStart)) && chatDuration != -1 && printText) || (!printText && !snd_voiceIsPlaying()))
break;
uint32 nextTime = loopStart + _tickLength;
@@ -137,8 +136,11 @@ void KyraEngine_LoK::endCharacterChat(int8 charNum, int16 convoInitialized) {
_charSayUnk3 = -1;
if (charNum > 4 && charNum < 11) {
- //TODO: weird _game_inventory stuff here
- //warning("STUB: endCharacterChat() for high charnums");
+ _animator->sprites()[_disabledTalkAnimObject].active = 1;
+ _sprites->_anims[_disabledTalkAnimObject].play = true;
+
+ _animator->sprites()[_enabledTalkAnimObject].active = 0;
+ _sprites->_anims[_enabledTalkAnimObject].play = false;
}
if (convoInitialized != 0) {
@@ -225,8 +227,19 @@ int KyraEngine_LoK::initCharacterChat(int8 charNum) {
_animator->restoreAllObjectBackgrounds();
if (charNum > 4 && charNum < 11) {
- // TODO: Fill in weird _game_inventory stuff here
- //warning("STUB: initCharacterChat() for high charnums");
+ const uint8 animDisableTable[] = { 3, 1, 1, 5, 0, 6 };
+ const uint8 animEnableTable[] = { 4, 2, 5, 6, 1, 7 };
+
+ _disabledTalkAnimObject = animDisableTable[charNum - 5];
+ _enabledTalkAnimObject = animEnableTable[charNum - 5];
+
+ _animator->sprites()[_disabledTalkAnimObject].active = 0;
+ _sprites->_anims[_disabledTalkAnimObject].play = false;
+
+ _animator->sprites()[_enabledTalkAnimObject].active = 1;
+ _sprites->_anims[_enabledTalkAnimObject].play = true;
+
+ _charSayUnk2 = _enabledTalkAnimObject;
}
_animator->flagAllObjectsForRefresh();
@@ -279,7 +292,9 @@ void KyraEngine_LoK::characterSays(int vocFile, const char *chatStr, int8 charNu
_text->_talkMessageY = yPos;
_text->_talkMessageH = lineNum * 10;
- if (textEnabled()) {
+ const bool printText = textEnabled();
+
+ if (printText) {
_animator->restoreAllObjectBackgrounds();
_screen->copyRegion(12, _text->_talkMessageY, 12, 136, 296, _text->_talkMessageH, 2, 2);
@@ -296,9 +311,9 @@ void KyraEngine_LoK::characterSays(int vocFile, const char *chatStr, int8 charNu
if (!speechEnabled())
vocFile = -1;
- waitForChatToFinish(vocFile, chatTicks, chatStr, charNum);
+ waitForChatToFinish(vocFile, chatTicks, chatStr, charNum, printText);
- if (textEnabled()) {
+ if (printText) {
_animator->restoreAllObjectBackgrounds();
_screen->copyRegion(12, 136, 12, _text->_talkMessageY, 296, _text->_talkMessageH, 2, 2);
diff --git a/engines/kyra/text_mr.cpp b/engines/kyra/text_mr.cpp
index ea2dc48031..726d9e339f 100644
--- a/engines/kyra/text_mr.cpp
+++ b/engines/kyra/text_mr.cpp
@@ -265,7 +265,8 @@ void KyraEngine_MR::objectChatInit(const char *str, int object, int vocHigh, int
_screen->hideMouse();
- if (textEnabled()) {
+ _chatTextEnabled = textEnabled();
+ if (_chatTextEnabled) {
objectChatPrintText(str, object);
_chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength;
} else {
diff --git a/engines/kyra/timer_hof.cpp b/engines/kyra/timer_hof.cpp
index 117b84f48a..d8f86e30a2 100644
--- a/engines/kyra/timer_hof.cpp
+++ b/engines/kyra/timer_hof.cpp
@@ -97,6 +97,9 @@ void KyraEngine_HoF::setTimer1DelaySecs(int secs) {
}
void KyraEngine_HoF::setWalkspeed(uint8 newSpeed) {
+ if (!_timer)
+ return;
+
if (newSpeed < 5)
newSpeed = 3;
else
diff --git a/engines/kyra/timer_lok.cpp b/engines/kyra/timer_lok.cpp
index 3a7d1ed0de..6f4948c279 100644
--- a/engines/kyra/timer_lok.cpp
+++ b/engines/kyra/timer_lok.cpp
@@ -48,33 +48,39 @@ void KyraEngine_LoK::setupTimers() {
for (int i = 10; i <= 13; ++i)
_timer->addTimer(i, 0, 420, 1);
- _timer->addTimer(14, TimerV1(timerCheckAnimFlag2), 600, 1);
+ _timer->addTimer(14, TimerV1(timerAsWillowispTimeout), 600, 1);
_timer->addTimer(15, TimerV1(timerUpdateHeadAnims), 11, 1);
- _timer->addTimer(16, TimerV1(timerSetFlags1), 7200, 1);
- _timer->addTimer(17, 0 /*sub_15120*/, 7200, 1);
- _timer->addTimer(18, TimerV1(timerCheckAnimFlag1), 600, 1);
+ _timer->addTimer(16, TimerV1(timerTulipCreator), 7200, 1);
+ _timer->addTimer(17, TimerV1(timerRubyCreator), 7200, 1);
+ _timer->addTimer(18, TimerV1(timerAsInvisibleTimeout), 600, 1);
_timer->addTimer(19, TimerV1(timerRedrawAmulet), 600, 1);
_timer->addTimer(20, 0, 7200, 1);
- _timer->addTimer(21, 0/*sub_1517C*/, 18000, 1);
+ _timer->addTimer(21, TimerV1(timerLavenderRoseCreator), 18000, 1);
_timer->addTimer(22, 0, 7200, 1);
- for (int i = 23; i <= 27; ++i)
- _timer->addTimer(i, 0, 10800, 1);
+ _timer->addTimer(23, 0, 10800, 1);
+ _timer->addTimer(24, TimerV1(timerAcornCreator), 10800, 1);
+ _timer->addTimer(25, 0, 10800, 1);
+ _timer->addTimer(26, TimerV1(timerBlueberryCreator), 10800, 1);
+ _timer->addTimer(27, 0, 10800, 1);
_timer->addTimer(28, 0, 21600, 1);
_timer->addTimer(29, 0, 7200, 1);
_timer->addTimer(30, 0, 10800, 1);
_timer->addTimer(31, TimerV1(timerFadeText), -1, 1);
- _timer->addTimer(32, TimerV1(updateAnimFlag1), 9, 1);
- _timer->addTimer(33, TimerV1(updateAnimFlag2), 3, 1);
+ _timer->addTimer(32, TimerV1(timerWillowispFrameTimer), 9, 1);
+ _timer->addTimer(33, TimerV1(timerInvisibleFrameTimer), 3, 1);
}
void KyraEngine_LoK::timerUpdateHeadAnims(int 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};
+ 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;
@@ -89,19 +95,51 @@ void KyraEngine_LoK::timerUpdateHeadAnims(int timerNum) {
_animator->animRefreshNPC(_talkingCharNum);
}
-void KyraEngine_LoK::timerSetFlags1(int timerNum) {
+void KyraEngine_LoK::timerTulipCreator(int timerNum) {
if (_currentCharacter->sceneId == 0x1C)
return;
- int rndNr = _rnd.getRandomNumberRng(0, 3);
+ setItemCreationFlags(17, 3);
+}
+
+void KyraEngine_LoK::timerRubyCreator(int timerNum) {
+ if (_currentCharacter->sceneId == 0x23)
+ return;
+
+ setItemCreationFlags(22, 4);
+}
+
+void KyraEngine_LoK::timerLavenderRoseCreator(int timerNum) {
+ if (_currentCharacter->sceneId == 0x06)
+ return;
+
+ setItemCreationFlags(0, 4);
+}
+
+void KyraEngine_LoK::timerAcornCreator(int timerNum) {
+ if (_currentCharacter->sceneId == 0x1F)
+ return;
+
+ setItemCreationFlags(72, 5);
+}
- for (int i = 0; i < 4; i++) {
- if (!queryGameFlag(rndNr + 17)) {
- setGameFlag(rndNr + 17);
+void KyraEngine_LoK::timerBlueberryCreator(int timerNum) {
+ if (_currentCharacter->sceneId == 0x28)
+ return;
+
+ setItemCreationFlags(26, 7);
+}
+
+void KyraEngine_LoK::setItemCreationFlags(int offset, int count) {
+ int rndNr = _rnd.getRandomNumberRng(0, count);
+
+ for (int i = 0; i <= count; i++) {
+ if (!queryGameFlag(rndNr + offset)) {
+ setGameFlag(rndNr + offset);
break;
} else {
rndNr++;
- if (rndNr > 3)
+ if (rndNr > count)
rndNr = 0;
}
}
@@ -111,16 +149,14 @@ void KyraEngine_LoK::timerFadeText(int timerNum) {
_fadeText = true;
}
-void KyraEngine_LoK::updateAnimFlag1(int timerNum) {
- if (_brandonStatusBit & 2) {
+void KyraEngine_LoK::timerWillowispFrameTimer(int timerNum) {
+ if (_brandonStatusBit & 2)
_brandonStatusBit0x02Flag = 1;
- }
}
-void KyraEngine_LoK::updateAnimFlag2(int timerNum) {
- if (_brandonStatusBit & 0x20) {
+void KyraEngine_LoK::timerInvisibleFrameTimer(int timerNum) {
+ if (_brandonStatusBit & 0x20)
_brandonStatusBit0x20Flag = 1;
- }
}
void KyraEngine_LoK::setTextFadeTimerCountdown(int16 countdown) {
@@ -130,19 +166,14 @@ void KyraEngine_LoK::setTextFadeTimerCountdown(int16 countdown) {
_timer->setCountdown(31, countdown*60);
}
-void KyraEngine_LoK::timerSetFlags2(int timerNum) {
- if (!((uint32 *)(_flagsTable+0x2D))[timerNum])
- ((uint32 *)(_flagsTable+0x2D))[timerNum] = 1;
-}
-
-void KyraEngine_LoK::timerCheckAnimFlag1(int timerNum) {
+void KyraEngine_LoK::timerAsInvisibleTimeout(int timerNum) {
if (_brandonStatusBit & 0x20) {
checkAmuletAnimFlags();
_timer->setCountdown(18, -1);
}
}
-void KyraEngine_LoK::timerCheckAnimFlag2(int timerNum) {
+void KyraEngine_LoK::timerAsWillowispTimeout(int timerNum) {
if (_brandonStatusBit & 0x2) {
checkAmuletAnimFlags();
_timer->setCountdown(14, -1);
@@ -157,6 +188,9 @@ void KyraEngine_LoK::timerRedrawAmulet(int timerNum) {
}
void KyraEngine_LoK::setWalkspeed(uint8 newSpeed) {
+ if (!_timer)
+ return;
+
static const uint8 speeds[] = { 11, 9, 6, 5, 3 };
assert(newSpeed < ARRAYSIZE(speeds));
diff --git a/engines/kyra/vqa.cpp b/engines/kyra/vqa.cpp
index 8a598591bb..d0e54f996b 100644
--- a/engines/kyra/vqa.cpp
+++ b/engines/kyra/vqa.cpp
@@ -260,8 +260,8 @@ bool VQAMovie::open(const char *filename) {
_header.bits = 8;
}
- setX((Screen::SCREEN_W - _header.width) / 2);
- setY((Screen::SCREEN_H - _header.height) / 2);
+ _x = (Screen::SCREEN_W - _header.width) / 2;
+ _y = (Screen::SCREEN_H - _header.height) / 2;
_frameInfo = new uint32[_header.numFrames];
_frame = new byte[_header.width * _header.height];
diff --git a/engines/kyra/vqa.h b/engines/kyra/vqa.h
index c1448a4865..129d526e98 100644
--- a/engines/kyra/vqa.h
+++ b/engines/kyra/vqa.h
@@ -56,9 +56,6 @@ public:
// It's unlikely that we ever want to change the movie position from
// its default.
- void setX(int x) { _x = x; }
- void setY(int y) { _y = y; }
-
void setDrawPage(int page) { _drawPage = page; }
bool open(const char *filename);
diff --git a/engines/lure/debugger.cpp b/engines/lure/debugger.cpp
index 3abc079a05..1cfe0804e4 100644
--- a/engines/lure/debugger.cpp
+++ b/engines/lure/debugger.cpp
@@ -105,7 +105,7 @@ bool Debugger::cmd_enterRoom(int argc, const char **argv) {
if (!remoteFlag)
res.getActiveHotspot(PLAYER_ID)->setRoomNumber(roomNumber);
- _detach_now = true;
+ detach();
return false;
}
diff --git a/engines/lure/detection.cpp b/engines/lure/detection.cpp
index 36c1cf237d..dd2a702e2a 100644
--- a/engines/lure/detection.cpp
+++ b/engines/lure/detection.cpp
@@ -196,7 +196,11 @@ static const ADParams detectionParams = {
// Flags
kADFlagUseExtraAsHint,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH
+ Common::GUIO_NOSPEECH,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class LureMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/lure/fights.cpp b/engines/lure/fights.cpp
index 53539677c8..789c9d924e 100644
--- a/engines/lure/fights.cpp
+++ b/engines/lure/fights.cpp
@@ -132,8 +132,7 @@ void FightsManager::fightLoop() {
}
Screen::getReference().update();
- if (game.debugger().isAttached())
- game.debugger().onFrame();
+ game.debugger().onFrame();
g_system->delayMillis(10);
}
diff --git a/engines/lure/game.cpp b/engines/lure/game.cpp
index d0f98b9c34..e77ac25716 100644
--- a/engines/lure/game.cpp
+++ b/engines/lure/game.cpp
@@ -56,8 +56,6 @@ Game::Game() {
_debugFlag = gDebugLevel >= ERROR_BASIC;
_soundFlag = true;
- _musicVolume = ConfMan.getBool("music_mute") ? 0 : MIN(255, ConfMan.getInt("music_volume"));
- _sfxVolume = ConfMan.getBool("sfx_mute") ? 0 : MIN(255, ConfMan.getInt("sfx_volume"));
}
Game::~Game() {
@@ -281,8 +279,7 @@ void Game::execute() {
system.updateScreen();
system.delayMillis(10);
- if (_debugger->isAttached())
- _debugger->onFrame();
+ _debugger->onFrame();
}
room.leaveRoom();
diff --git a/engines/lure/game.h b/engines/lure/game.h
index 3864e9c205..123ac0dca7 100644
--- a/engines/lure/game.h
+++ b/engines/lure/game.h
@@ -48,8 +48,6 @@ class Game {
private:
Debugger *_debugger;
bool _fastTextFlag, _soundFlag;
- uint8 _sfxVolume;
- uint8 _musicVolume;
uint8 _state;
uint16 _tellCommands[MAX_TELL_COMMANDS * 3 + 1];
int _numTellCommands;
@@ -87,8 +85,6 @@ public:
bool &debugFlag() { return _debugFlag; }
bool fastTextFlag() { return _fastTextFlag; }
bool soundFlag() { return _soundFlag; }
- uint8 sfxVolume() { return ConfMan.getInt("sfx_volume"); }
- uint8 musicVolume() { return ConfMan.getInt("music_volume"); }
Debugger &debugger() { return *_debugger; }
// Menu item support methods
diff --git a/engines/lure/sound.cpp b/engines/lure/sound.cpp
index e725b7c31a..a75545c330 100644
--- a/engines/lure/sound.cpp
+++ b/engines/lure/sound.cpp
@@ -50,13 +50,13 @@ SoundManager::SoundManager() {
_soundData = NULL;
_paused = false;
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _isRoland = midiDriver != MD_ADLIB;
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
+ _isRoland = MidiDriver::getMusicType(dev) != MT_ADLIB;
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
Common::set_to(_channelsInUse, _channelsInUse + NUM_CHANNELS, false);
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
int statusCode = _driver->open();
if (statusCode) {
warning("Sound driver returned error code %d", statusCode);
@@ -72,6 +72,8 @@ SoundManager::SoundManager() {
_channelsInner[index].volume = 90;
}
}
+
+ syncSounds();
}
SoundManager::~SoundManager() {
@@ -288,16 +290,21 @@ uint8 SoundManager::descIndexOf(uint8 soundNumber) {
// Used to sync the volume for all channels with the Config Manager
//
void SoundManager::syncSounds() {
- Game &game = Game::getReference();
musicInterface_TidySounds();
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+ _musicVolume = mute ? 0 : MIN(255, ConfMan.getInt("music_volume"));
+ _sfxVolume = mute ? 0 : MIN(255, ConfMan.getInt("sfx_volume"));
+
g_system->lockMutex(_soundMutex);
MusicListIterator i;
for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) {
if ((*i)->isMusic())
- (*i)->setVolume(game.musicVolume());
+ (*i)->setVolume(_musicVolume);
else
- (*i)->setVolume(game.sfxVolume());
+ (*i)->setVolume(_sfxVolume);
}
g_system->unlockMutex(_soundMutex);
}
@@ -599,9 +606,9 @@ MidiMusic::MidiMusic(MidiDriver *driver, ChannelEntry channels[NUM_CHANNELS],
}
if (_isMusic)
- setVolume(ConfMan.getInt("music_volume"));
+ setVolume(Sound.musicVolume());
else
- setVolume(ConfMan.getInt("sfx_volume"));
+ setVolume(Sound.sfxVolume());
_passThrough = false;
@@ -658,8 +665,7 @@ void MidiMusic::setVolume(int volume) {
_volume = volume;
- Game &game = Game::getReference();
- volume *= _isMusic ? game.musicVolume() : game.sfxVolume();
+ volume *= _isMusic ? Sound.musicVolume() : Sound.sfxVolume();
for (int i = 0; i < _numChannels; ++i) {
if (_channels[_channelNumber + i].midiChannel != NULL)
@@ -707,8 +713,7 @@ void MidiMusic::send(uint32 b) {
// Adjust volume changes by song and master volume
byte volume = (byte)((b >> 16) & 0x7F);
_channels[channel].volume = volume;
- Game &game = Game::getReference();
- int master_volume = _isMusic ? game.musicVolume() : game.sfxVolume();
+ int master_volume = _isMusic ? Sound.musicVolume() : Sound.sfxVolume();
volume = volume * _volume * master_volume / 65025;
b = (b & 0xFF00FFFF) | (volume << 16);
} else if ((b & 0xF0) == 0xC0) {
diff --git a/engines/lure/sound.h b/engines/lure/sound.h
index c41cec48fe..6d248fbd20 100644
--- a/engines/lure/sound.h
+++ b/engines/lure/sound.h
@@ -105,7 +105,7 @@ public:
bool isMusic() {return _isMusic; }
};
-class SoundManager: public Common::Singleton<SoundManager> {
+class SoundManager : public Common::Singleton<SoundManager> {
private:
// Outer sound interface properties
MemoryBlock *_descs;
@@ -128,11 +128,15 @@ private:
Common::MutexRef _soundMutex;
bool _paused;
+ uint _musicVolume;
+ uint _sfxVolume;
+
// Internal support methods
void bellsBodge();
void musicInterface_TidySounds();
static void onTimer(void *data);
void doTimer();
+
public:
SoundManager();
~SoundManager();
@@ -156,9 +160,11 @@ public:
void fadeOut();
void pause() { _paused = true; }
void resume() { _paused = false; }
- bool getPaused() { return _paused; }
- bool hasNativeMT32() { return _nativeMT32; }
- bool isRoland() { return _isRoland; }
+ bool getPaused() const { return _paused; }
+ bool hasNativeMT32() const { return _nativeMT32; }
+ bool isRoland() const { return _isRoland; }
+ uint musicVolume() const { return _musicVolume; }
+ uint sfxVolume() const { return _sfxVolume; }
// The following methods implement the external sound player module
void musicInterface_Initialise();
diff --git a/engines/m4/animation.cpp b/engines/m4/animation.cpp
index fe46e121f0..37314eff44 100644
--- a/engines/m4/animation.cpp
+++ b/engines/m4/animation.cpp
@@ -26,181 +26,510 @@
#include "m4/assets.h"
#include "m4/animation.h"
#include "m4/compression.h"
+#include "m4/mads_scene.h"
namespace M4 {
// TODO: this code needs cleanup
-Animation::Animation(MadsM4Engine *vm) {
- _vm = vm;
- _playing = false;
+MadsAnimation::MadsAnimation(MadsM4Engine *vm, MadsView *view): Animation(vm), _view(view) {
+ _font = NULL;
+ _resetFlag = false;
+ _freeFlag = false;
+ _skipLoad = false;
+ _unkIndex = -1;
+ _messageCtr= 0;
+ _field12 = 0;
+
+ _currentFrame = 0;
+ _oldFrameEntry = 0;
+ _nextFrameTimer = _madsVm->_currentTimer;
+ _nextScrollTimer = 0;
}
-void Animation::loadFullScreen(const char *filename) {
- _vm->_palette->deleteAllRanges();
- load(filename);
+MadsAnimation::~MadsAnimation() {
+ for (uint i = 0; i < _messages.size(); ++i) {
+ if (_messages[i].kernelMsgIndex >= 0)
+ _view->_kernelMessages.remove(_messages[i].kernelMsgIndex);
+ }
+
+ // Further deletion logic
+ if (_field12) {
+ _view->_spriteSlots.deleteSprites(_spriteListIndexes[_spriteListIndex]);
+ }
}
-void Animation::load(const char *filename) {
- MadsPack anim(filename, _vm);
+#define FILENAME_SIZE 13
+
+/**
+ * Initialises and loads the data of an animation
+ */
+void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *surface, M4Surface *depthSurface) {
+ MadsPack anim(filename.c_str(), _vm);
+ bool madsRes = filename[0] == '*';
char buffer[20];
+ int streamIndex = 1;
// Chunk 1: header
// header
- // TODO: there are some unknown fields here, plus we don't read
- // the entire chunk
+
Common::SeekableReadStream *animStream = anim.getItemStream(0);
- Common::SeekableReadStream *spriteSeriesStream;
- //printf("Chunk 0, size %i\n", animStream->size());
- _seriesCount = animStream->readUint16LE();
- _frameCount = animStream->readUint16LE();
- _frameEntryCount = animStream->readUint16LE();
-
- // Unknown
- for (int i = 0; i < 43; i++)
- animStream->readByte();
-
- _spriteSeriesNames = new Common::String[_seriesCount];
- printf("%i sprite series\n", _seriesCount);
-
- // TODO: for now, we only load the first sprite series
- if (_seriesCount > 1)
- printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount);
- _seriesCount = 1; // TODO
-
- for (int i = 0; i < _seriesCount; i++) {
- animStream->read(buffer, 13);
- _spriteSeriesNames[i] = Common::String(buffer);
- //printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str());
-
- spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str());
- _spriteSeries = new SpriteAsset(_vm, spriteSeriesStream,
- spriteSeriesStream->size(), _spriteSeriesNames[i].c_str());
- _vm->res()->toss(_spriteSeriesNames[i].c_str());
-
- // Adjust the palette of the sprites in the sprite series
- // so that they can be displayed on screen correctly
- RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true);
- _vm->_palette->addRange(palData);
-
- for (int k = 0; k < _spriteSeries->getCount(); k++) {
- M4Sprite *spr = _spriteSeries->getFrame(k);
- spr->translate(palData); // sprite pixel translation
- }
+
+ int spriteListCount = animStream->readUint16LE();
+ int miscEntriesCount = animStream->readUint16LE();
+ int frameEntryCount = animStream->readUint16LE();
+ int messagesCount = animStream->readUint16LE();
+ animStream->skip(1);
+ _flags = animStream->readByte();
+
+ animStream->skip(2);
+ _animMode = animStream->readUint16LE();
+ _roomNumber = animStream->readUint16LE();
+ animStream->skip(2);
+ _field12 = animStream->readUint16LE() != 0;
+ _spriteListIndex = animStream->readUint16LE();
+ _scrollX = animStream->readSint16LE();
+ _scrollY = animStream->readSint16LE();
+ _scrollTicks = animStream->readUint16LE();
+ animStream->skip(8);
+
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _interfaceFile = Common::String(buffer);
+
+ for (int i = 0; i < 10; ++i) {
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _spriteSetNames[i] = Common::String(buffer);
}
- //printf("End pos: %i\n", animStream->pos());
+ animStream->skip(81);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _lbmFilename = Common::String(buffer);
- delete animStream;
+ animStream->skip(365);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _spritesFilename = Common::String(buffer);
+
+ animStream->skip(48);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _soundName = Common::String(buffer);
- // ------------------
-
- // Chunk 2: anim info
- AnimationFrame frame;
- animStream = anim.getItemStream(1);
- //printf("Chunk 1, size %i\n", animStream->size());
-
- _frameEntries = new AnimationFrame[_frameEntryCount];
-
- for (int i = 0; i < _frameEntryCount; i++) {
-
- frame.animFrameIndex = animStream->readUint16LE();
- frame.u = animStream->readByte();
- frame.seriesIndex = animStream->readByte();
- frame.seriesFrameIndex = animStream->readUint16LE();
- frame.x = animStream->readUint16LE();
- frame.y = animStream->readUint16LE();
- frame.v = animStream->readByte();
- frame.w = animStream->readByte();
-
- _frameEntries[i] = frame;
-
- /*
- printf(
- "animFrameIndex = %4d, "
- "u = %3d, "
- "seriesIndex = %3d, "
- "seriesFrameIndex = %6d, "
- "x = %3d, "
- "y = %3d, "
- "v = %3d, "
- "w = %3d\n",
-
- frame.animFrameIndex,
- frame.u,
- frame.seriesIndex,
- frame.seriesFrameIndex,
- frame.x,
- frame.y,
- frame.v,
- frame.w
- );
- */
- }
- //printf("End pos: %i\n", animStream->pos());
+ animStream->skip(13);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _dsrName = Common::String(buffer);
+
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ Common::String fontResource(buffer);
+
+ if (_animMode == 4)
+ flags |= 0x4000;
+ if (flags & 0x100)
+ loadInterface(surface, depthSurface);
+
+ // Initialise the reference list
+ for (int i = 0; i < spriteListCount; ++i)
+ _spriteListIndexes.push_back(-1);
delete animStream;
- // Chunk 3: unknown (seems to be sound data?)
- // TODO
-}
+ if (messagesCount > 0) {
+ // Chunk 2
+ // Following is a list of any messages for the animation
+
+ animStream = anim.getItemStream(streamIndex++);
+
+ for (int i = 0; i < messagesCount; ++i) {
+ AnimMessage rec;
+ rec.soundId = animStream->readSint16LE();
+ animStream->read(rec.msg, 64);
+ animStream->skip(4);
+ rec.pos.x = animStream->readSint16LE();
+ rec.pos.y = animStream->readSint16LE();
+ rec.flags = animStream->readUint16LE();
+ rec.rgb1.r = animStream->readByte() << 2;
+ rec.rgb1.g = animStream->readByte() << 2;
+ rec.rgb1.b = animStream->readByte() << 2;
+ rec.rgb2.r = animStream->readByte() << 2;
+ rec.rgb2.g = animStream->readByte() << 2;
+ rec.rgb2.b = animStream->readByte() << 2;
+ animStream->skip(2); // Space for kernelMsgIndex
+ rec.kernelMsgIndex = -1;
+ animStream->skip(6);
+ rec.startFrame = animStream->readUint16LE();
+ rec.endFrame = animStream->readUint16LE();
+ animStream->skip(2);
+
+ _messages.push_back(rec);
+ }
-Animation::~Animation() {
- //delete[] _spriteSeriesNames;
- //delete[] _spriteSeries;
- //delete[] _frameEntries;
+ delete animStream;
+ }
+
+ if (frameEntryCount > 0) {
+ // Chunk 3: animation frame info
+ animStream = anim.getItemStream(streamIndex++);
+
+ for (int i = 0; i < frameEntryCount; i++) {
+ AnimFrameEntry rec;
+ rec.frameNumber = animStream->readUint16LE();
+ rec.seqIndex = animStream->readByte();
+ rec.spriteSlot.spriteListIndex = animStream->readByte();
+ rec.spriteSlot.frameNumber = animStream->readUint16LE();
+ rec.spriteSlot.xp = animStream->readSint16LE();
+ rec.spriteSlot.yp = animStream->readSint16LE();
+ rec.spriteSlot.depth = animStream->readSByte();
+ rec.spriteSlot.scale = (int8)animStream->readByte();
+
+ _frameEntries.push_back(rec);
+ }
+
+ delete animStream;
+ }
+
+ if (miscEntriesCount > 0) {
+ // Chunk 4: Misc Data
+ animStream = anim.getItemStream(streamIndex);
+
+ for (int i = 0; i < miscEntriesCount; ++i) {
+ AnimMiscEntry rec;
+ rec.soundNum = animStream->readByte();
+ rec.msgIndex = animStream->readSByte();
+ rec.numTicks = animStream->readUint16LE();
+ rec.posAdjust.x = animStream->readUint16LE();
+ rec.posAdjust.y = animStream->readUint16LE();
+ animStream->readUint16LE();
+
+ _miscEntries.push_back(rec);
+ }
+
+ delete animStream;
+ }
+
+ // If the animation specifies a font, then load it for access
+ if (_flags & ANIM_CUSTOM_FONT) {
+ Common::String fontName;
+ if (madsRes)
+ fontName += "*";
+ fontName += fontResource;
+
+ if (fontName != "")
+ _font = _vm->_font->getFont(fontName.c_str());
+ else
+ warning("Attempted to set a font with an empty name");
+ }
+
+ // If a speech file is specified, then load it
+ if (!_dsrName.empty())
+ _vm->_sound->loadDSRFile(_dsrName.c_str());
+
+ // Load all the sprite sets for the animation
+ for (int i = 0; i < spriteListCount; ++i) {
+ if (_field12 && (i == _spriteListIndex))
+ // Skip over field, since it's manually loaded
+ continue;
+
+ _spriteListIndexes[i] = _view->_spriteSlots.addSprites(_spriteSetNames[i].c_str());
+ }
+
+
+ if (_field12) {
+ Common::String resName;
+ if (madsRes)
+ resName += "*";
+ resName += _spriteSetNames[_spriteListIndex];
+
+ _spriteListIndexes[_spriteListIndex] = _view->_spriteSlots.addSprites(resName.c_str());
+ }
+
+ // TODO: Unknown section about handling sprite set list combined with messages size
+
+ // TODO: The original has two separate loops for the loop below based on _animMode == 4. Is it
+ // perhaps that in that mode the sprite frames has a different format..?
+
+ // Remap the sprite list index fields from the initial value to the indexes of the loaded
+ // sprite sets for the animation
+ for (uint i = 0; i < _frameEntries.size(); ++i) {
+ int idx = _frameEntries[i].spriteSlot.spriteListIndex;
+ _frameEntries[i].spriteSlot.spriteListIndex = _spriteListIndexes[idx];
+ }
+
+ if (hasScroll())
+ _nextScrollTimer = _madsVm->_currentTimer + _scrollTicks;
}
-void Animation::start() {
- _curFrame = 0;
- _curFrameEntry = 0;
- //for (int i = 0; i < _seriesCount; i++) {
- //_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str());
- //}
- _playing = true;
- updateAnim();
+/**
+ * Loads an animation file for display
+ */
+void MadsAnimation::load(const Common::String &filename, int abortTimers) {
+ initialise(filename, 0, NULL, NULL);
+ _messageCtr = 0;
+ _skipLoad = true;
+
+/* TODO: figure out extra stuff in this routine
+ if (_field12) {
+ _unkIndex = -1;
+ int listIndex = _spriteListIndexes[_spriteListIndex];
+ SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex);
+ ..?..
+ }
+*/
+
+ // Initialise miscellaneous fields
+ _currentFrame = 0;
+ _oldFrameEntry = 0;
+ _nextFrameTimer = _madsVm->_currentTimer;
+ _abortTimers = abortTimers;
+ _abortMode = _madsVm->scene()->_abortTimersMode2;
+
+ if (_madsVm->_scene)
+ _actionNouns = _madsVm->scene()->_action._action;
+
+ // Initialise kernel message list
+ for (uint i = 0; i < _messages.size(); ++i)
+ _messages[i].kernelMsgIndex = -1;
}
-bool Animation::updateAnim() {
- if (!_playing)
- return true;
+void MadsAnimation::update() {
+ if (_field12) {
+ int spriteListIndex = _spriteListIndexes[_spriteListIndex];
+ int newIndex = -1;
+
+ for (uint idx = _oldFrameEntry; idx < _frameEntries.size(); ++idx) {
+ if (_frameEntries[idx].frameNumber > _currentFrame)
+ break;
+ if (_frameEntries[idx].spriteSlot.spriteListIndex == spriteListIndex)
+ newIndex = _frameEntries[idx].spriteSlot.frameNumber;
+ }
+
+ if (newIndex >= 0)
+ load1(newIndex);
+ }
+
+ // Check for scroll change
+ bool screenChanged = false;
+
+ // Handle any scrolling of the screen surface
+ if (hasScroll() && (_madsVm->_currentTimer >= _nextScrollTimer)) {
+ _view->_bgSurface->scrollX(_scrollX);
+ _view->_bgSurface->scrollY(_scrollY);
+
+ _nextScrollTimer = _madsVm->_currentTimer + _scrollTicks;
+ screenChanged = true;
+ }
+
+ // If it's not time for the next frame, then exit
+ if (_madsVm->_currentTimer < _nextFrameTimer) {
+ if (screenChanged)
+ _view->_spriteSlots.fullRefresh();
+ return;
+ }
+
+ // Loop checks for any prior animation sprite slots to be expired
+ for (int slotIndex = 0; slotIndex < _view->_spriteSlots.startIndex; ++slotIndex) {
+ if (_view->_spriteSlots[slotIndex].seqIndex >= 0x80) {
+ // Flag the frame as animation sprite slot
+ _view->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE;
+ }
+ }
+
+ // Validate the current frame
+ if (_currentFrame >= (int)_miscEntries.size()) {
+ // Is the animation allowed to be repeated?
+ if (_resetFlag) {
+ _currentFrame = 0;
+ _oldFrameEntry = 0;
+ } else {
+ _freeFlag = true;
+ return;
+ }
+ }
- // Get the scene background surface
- M4Surface *bg = _vm->_scene->getBackgroundSurface();
+ // Handle starting any sound for this frame
+ AnimMiscEntry &misc = _miscEntries[_currentFrame];
+ if (misc.soundNum)
+ _vm->_sound->playSound(misc.soundNum);
- while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) {
- AnimationFrame *frame = &_frameEntries[_curFrameEntry];
- int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1;
+ // Handle any offset adjustment for sprites as of this frame
+ if (_view->_posAdjust.x != misc.posAdjust.x) {
+ _view->_posAdjust.x = misc.posAdjust.x;
+ screenChanged = true;
+ }
+ if (_view->_posAdjust.y != misc.posAdjust.y) {
+ _view->_posAdjust.y = misc.posAdjust.y;
+ screenChanged = true;
+ }
- // Write the sprite onto the screen
- M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex);
- // FIXME: correct x, y
- spr->copyTo(bg, frame->x, frame->y, (int)spr->getTransparentColor());
+ if (screenChanged) {
+ // Signal the entire screen needs refreshing
+ _view->_spriteSlots.fullRefresh();
+ }
- // HACK: wait a bit
- g_system->delayMillis(100);
+ int spriteSlotsMax = _view->_spriteSlots.startIndex;
+
+ // Main frame animation loop - frames get animated by being placed, as necessary, into the
+ // main sprite slot array
+ while ((uint)_oldFrameEntry < _frameEntries.size()) {
+ if (_frameEntries[_oldFrameEntry].frameNumber > _currentFrame)
+ break;
+ else if (_frameEntries[_oldFrameEntry].frameNumber == _currentFrame) {
+ // Found the correct frame
+ int spriteSlotIndex = 0;
+ int index = 0;
+
+ for (;;) {
+ if ((spriteSlotIndex == 0) && (index < spriteSlotsMax)) {
+ int seqIndex = _frameEntries[_oldFrameEntry].seqIndex - _view->_spriteSlots[index].seqIndex;
+ if (seqIndex == 0x80) {
+ if (_view->_spriteSlots[index] == _frameEntries[_oldFrameEntry].spriteSlot) {
+ _view->_spriteSlots[index].spriteType = SPRITE_ZERO;
+ spriteSlotIndex = -1;
+ }
+ }
+ ++index;
+ continue;
+ }
+
+ if (spriteSlotIndex == 0) {
+ int slotIndex = _view->_spriteSlots.getIndex();
+ MadsSpriteSlot &slot = _view->_spriteSlots[slotIndex];
+ slot.copy(_frameEntries[_oldFrameEntry].spriteSlot);
+ slot.seqIndex = _frameEntries[_oldFrameEntry].seqIndex + 0x80;
+
+ SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(
+ _view->_spriteSlots[slotIndex].spriteListIndex);
+ slot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE;
+ }
+ break;
+ }
+ }
+
+ ++_oldFrameEntry;
+ }
- //printf("_curFrameEntry = %d\n", _curFrameEntry);
- _curFrameEntry++;
+ // Handle the display of any messages
+ for (uint idx = 0; idx < _messages.size(); ++idx) {
+ if (_messages[idx].kernelMsgIndex >= 0) {
+ // Handle currently active message
+ if ((_currentFrame < _messages[idx].startFrame) || (_currentFrame > _messages[idx].endFrame)) {
+ _view->_kernelMessages.remove(_messages[idx].kernelMsgIndex);
+ _messages[idx].kernelMsgIndex = -1;
+ --_messageCtr;
+ }
+ } else if ((_currentFrame >= _messages[idx].startFrame) && (_currentFrame <= _messages[idx].endFrame)) {
+ // Start displaying the message
+ AnimMessage &me = _messages[idx];
+
+ // The colour index to use is dependant on how many messages are currently on-screen
+ uint8 colIndex;
+ switch (_messageCtr) {
+ case 1:
+ colIndex = 252;
+ break;
+ case 2:
+ colIndex = 16;
+ break;
+ default:
+ colIndex = 250;
+ break;
+ }
+
+ _vm->_palette->setEntry(colIndex, me.rgb1.r, me.rgb1.g, me.rgb1.b);
+ _vm->_palette->setEntry(colIndex + 1, me.rgb2.r, me.rgb2.g, me.rgb2.b);
+
+ // Add a kernel message to display the given text
+ me.kernelMsgIndex = _view->_kernelMessages.add(me.pos, colIndex * 0x101 + 0x100, 0, 0, INDEFINITE_TIMEOUT, me.msg);
+ assert(me.kernelMsgIndex >= 0);
+
+ // Play the associated sound, if it exists
+ if (me.soundId > 0)
+ _vm->_sound->playDSRSound(me.soundId - 1, 255, false);
+ ++_messageCtr;
+ }
}
- //printf("_curFrame = %d\n", _curFrame);
+ // Move to the next frame
+ _currentFrame++;
+ if (_currentFrame >= (int)_miscEntries.size()) {
+ // Animation is complete
+ if (_abortTimers != 0) {
+ _view->_abortTimers = _abortTimers;
+ _view->_abortTimersMode = _abortMode;
+
+ if (_abortMode != ABORTMODE_1) {
+ // Copy the noun list
+ if (_madsVm->_scene)
+ _madsVm->scene()->_action._action = _actionNouns;
+ }
+ }
+ }
- _curFrame++;
- if (_curFrame >= _frameCount) // anim done
- stop();
+ int frameNum = MIN(_currentFrame, (int)_miscEntries.size() - 1);
+ _nextFrameTimer = _madsVm->_currentTimer + _miscEntries[frameNum].numTicks;
+}
+
+void MadsAnimation::setCurrentFrame(int frameNumber) {
+ _currentFrame = frameNumber;
+ _oldFrameEntry = 0;
+ _freeFlag = false;
- return _curFrame >= _frameCount;
+ _nextScrollTimer = _nextFrameTimer = _madsVm->_currentTimer;
}
-void Animation::stop() {
- _playing = false;
+int MadsAnimation::getCurrentFrame() {
+ return _currentFrame;
+}
+
+void MadsAnimation::load1(int frameNumber) {
+ if (_skipLoad)
+ return;
+
+ Common::Point pt;
+ int listIndex = _spriteListIndexes[_spriteListIndex];
+ SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex);
+
+ if (_unkIndex < 0) {
+ M4Surface *frame = spriteSet.getFrame(0);
+ pt.x = frame->bounds().left;
+ pt.y = frame->bounds().top;
+ } else {
+ pt.x = _unkList[_unkIndex].x;
+ pt.y = _unkList[_unkIndex].y;
+ _unkIndex = 1 - _unkIndex;
+ }
+
+ if (proc1(spriteSet, pt, frameNumber))
+ error("proc1 failure");
+}
+
+bool MadsAnimation::proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber) {
+ return 0;
+}
- for (int i = 0; i < _seriesCount; i++) {
- // TODO: cleanup
- //delete _spriteSeries[i];
- //_spriteSeries[i] = NULL;
+void MadsAnimation::loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface) {
+ if (_animMode <= 2) {
+ MadsSceneResources sceneResources;
+ sceneResources.load(_roomNumber, _interfaceFile.c_str(), 0, depthSurface, interfaceSurface);
+
+ } else if (_animMode == 4) {
+ // Load a scene interface
+ interfaceSurface->madsLoadInterface(_interfaceFile);
+ } else {
+ // This mode allocates two large surfaces for the animation
+ // TODO: Are these ever properly freed?
+error("Anim mode %d - need to check free logic", _animMode);
+ assert(!interfaceSurface);
+ assert(!depthSurface);
+ depthSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT);
+ interfaceSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT);
+ depthSurface->clear();
+ interfaceSurface->clear();
}
}
diff --git a/engines/m4/animation.h b/engines/m4/animation.h
index 2dfe0d887e..a7a6b57c32 100644
--- a/engines/m4/animation.h
+++ b/engines/m4/animation.h
@@ -29,41 +29,100 @@
#include "m4/m4.h"
#include "m4/graphics.h"
#include "m4/assets.h"
+#include "m4/mads_views.h"
+#include "common/array.h"
namespace M4 {
-struct AnimationFrame {
- uint16 animFrameIndex;
- byte u;
- byte seriesIndex;
- uint16 seriesFrameIndex;
- uint16 x, y;
- byte v, w;
+class MadsView;
+class SpriteSlotSubset;
+
+class AnimMessage {
+public:
+ int16 soundId;
+ char msg[64];
+ Common::Point pos;
+ RGB8 rgb1, rgb2;
+ uint16 flags;
+ int startFrame, endFrame;
+ int kernelMsgIndex;
+};
+
+class AnimFrameEntry {
+public:
+ int frameNumber;
+ int seqIndex;
+ SpriteSlotSubset spriteSlot;
};
-class Animation {
- public:
- Animation(MadsM4Engine *vm);
- ~Animation();
-
- void load(const char *filename);
- void loadFullScreen(const char *filename);
- void start();
- bool updateAnim();
- void stop();
-
- private:
- bool _playing;
- MadsM4Engine *_vm;
- int _seriesCount;
- int _frameCount;
- int _frameEntryCount;
- AnimationFrame *_frameEntries;
- Common::String *_spriteSeriesNames;
- SpriteAsset *_spriteSeries;
- int _curFrame, _curFrameEntry;
+class AnimMiscEntry {
+public:
+ int soundNum;
+ int msgIndex;
+ int numTicks;
+ Common::Point posAdjust;
+};
+
+#define ANIM_SPRITE_SET_SIZE 50
+
+enum MadsAnimationFlags {ANIM_CUSTOM_FONT = 0x20, ANIM_HAS_SOUND = 0x8000};
+
+class MadsAnimation: public Animation {
+private:
+ MadsView *_view;
+
+ int _spriteListCount;
+ Common::Array<AnimMessage> _messages;
+ Common::Array<AnimFrameEntry> _frameEntries;
+ Common::Array<AnimMiscEntry> _miscEntries;
+ Font *_font;
+
+ uint8 _flags;
+ int _animMode;
+ int _roomNumber;
+ bool _field12;
+ int _spriteListIndex;
+ int _scrollX;
+ int _scrollY;
+ int _scrollTicks;
+ Common::String _interfaceFile;
+ Common::String _spriteSetNames[10];
+ Common::String _lbmFilename;
+ Common::String _spritesFilename;
+ Common::String _soundName;
+ Common::String _dsrName;
+ Common::Array<int> _spriteListIndexes;
+
+ int _currentFrame, _oldFrameEntry;
+ bool _resetFlag;
+ bool _freeFlag;
+ bool _skipLoad;
+ int _unkIndex;
+ Common::Point _unkList[2];
+ uint32 _nextFrameTimer;
+ uint32 _nextScrollTimer;
+ int _messageCtr;
+ int _abortTimers;
+ AbortTimerMode _abortMode;
+ ActionDetails _actionNouns;
+
+ void load1(int frameNumber);
+ bool proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber);
+ void loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface);
+ bool hasScroll() const { return (_scrollX != 0) || (_scrollY != 0); }
+public:
+ MadsAnimation(MadsM4Engine *vm, MadsView *view);
+ virtual ~MadsAnimation();
+
+ virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *surface, M4Surface *depthSurface);
+ virtual void load(const Common::String &filename, int abortTimers);
+ virtual void update();
+ virtual void setCurrentFrame(int frameNumber);
+ virtual int getCurrentFrame();
bool freeFlag() const { return _freeFlag; }
+ bool getAnimMode() const { return _animMode; }
+ int roomNumber() const { return _roomNumber; }
};
} // End of namespace M4
diff --git a/engines/m4/assets.cpp b/engines/m4/assets.cpp
index e604019901..23122eb960 100644
--- a/engines/m4/assets.cpp
+++ b/engines/m4/assets.cpp
@@ -30,13 +30,13 @@
namespace M4 {
-BaseAsset::BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : _vm(vm) {
+BaseAsset::BaseAsset(MadsM4Engine *vm) : _vm(vm) {
}
BaseAsset::~BaseAsset() {
}
-MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
+MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
uint32 stateCount = stream->readUint32LE();
for (uint32 curState = 0; curState < stateCount; curState++) {
uint32 stateOffset = stream->readUint32LE();
@@ -61,7 +61,7 @@ uint32 MachineAsset::getStateOffset(uint32 state) {
return _stateTable[state];
}
-SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
+SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
_localVarCount = stream->readUint32LE();
_codeSize = size - 4;
_code = new byte[_codeSize];
@@ -78,7 +78,7 @@ void SequenceAsset::getCode(byte *&code, uint32 &codeSize) {
}
-DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
+DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
_recCount = stream->readUint32LE();
_recSize = stream->readUint32LE();
@@ -98,7 +98,9 @@ long *DataAsset::getRow(int index) {
return &_data[_recSize * index];
}
-SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : BaseAsset(vm, stream, size, name) {
+SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name,
+ bool asStream, int flags) :
+ BaseAsset(vm) {
_stream = stream;
_palInterface = NULL;
_paletteData = NULL;
@@ -106,10 +108,24 @@ SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, i
if (_vm->isM4()) {
loadM4SpriteAsset(vm, stream, asStream);
} else {
- loadMadsSpriteAsset(vm, stream);
+ loadMadsSpriteAsset(vm, stream, flags);
}
}
+SpriteAsset::SpriteAsset(MadsM4Engine *vm, const char *name): BaseAsset(vm) {
+ _stream = vm->res()->get(name);
+ _palInterface = NULL;
+ _paletteData = NULL;
+
+ if (_vm->isM4()) {
+ loadM4SpriteAsset(vm, _stream, true);
+ } else {
+ loadMadsSpriteAsset(vm, _stream, 0);
+ }
+
+ vm->res()->toss(name);
+}
+
SpriteAsset::~SpriteAsset() {
if (_palInterface) {
// Internally stored palette translation data, so release it
@@ -121,6 +137,8 @@ SpriteAsset::~SpriteAsset() {
for (Common::Array<SpriteAssetFrame>::iterator it = _frames.begin(); it != _frames.end(); ++it) {
delete (*it).frame;
}
+
+ delete _charInfo;
}
void SpriteAsset::loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream) {
@@ -185,7 +203,7 @@ void SpriteAsset::loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream
}
-void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream) {
+void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int flags) {
int curFrame = 0;
uint32 frameOffset = 0;
MadsPack sprite(stream);
@@ -195,11 +213,19 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
_maxHeight = 0;
Common::SeekableReadStream *spriteStream = sprite.getItemStream(0);
- for (int i = 0; i < 19; i++) {
- spriteStream->readUint16LE();
- }
+ _mode = spriteStream->readByte();
+ spriteStream->skip(1);
+ int type1 = spriteStream->readUint16LE();
+ int type2 = spriteStream->readUint16LE();
+ _isBackground = (type1 != 0) && (type2 < 4);
+ spriteStream->skip(32);
_frameCount = spriteStream->readUint16LE();
- // we skip the rest of the data
+
+ if (_vm->isM4() || ((flags & SPRITE_SET_CHAR_INFO) == 0))
+ _charInfo = NULL;
+ else
+ _charInfo = new MadsSpriteSetCharInfo(spriteStream);
+
delete spriteStream;
// Get the palette data
@@ -216,12 +242,15 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
spriteStream = sprite.getItemStream(1);
Common::SeekableReadStream *spriteDataStream = sprite.getItemStream(3);
SpriteAssetFrame frame;
+ Common::Array<int> frameSizes;
for (curFrame = 0; curFrame < _frameCount; curFrame++) {
frame.stream = 0;
frame.comp = 0;
frameOffset = spriteStream->readUint32LE();
_frameOffsets.push_back(frameOffset);
- spriteStream->readUint32LE(); // frame size
+ uint32 frameSize = spriteStream->readUint32LE();
+ frameSizes.push_back(frameSize);
+
frame.x = spriteStream->readUint16LE();
frame.y = spriteStream->readUint16LE();
frame.w = spriteStream->readUint16LE();
@@ -229,9 +258,44 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
if (curFrame == 0)
debugC(1, kDebugGraphics, "%i frames, x = %i, y = %i, w = %i, h = %i\n", _frameCount, frame.x, frame.y, frame.w, frame.h);
- frame.frame = new M4Sprite(spriteDataStream, frame.x, frame.y, frame.w, frame.h, false);
+ if (_mode == 0) {
+ // Create a frame and decompress the raw pixel data
+ uint32 currPos = (uint32)spriteDataStream->pos();
+ frame.frame = new M4Sprite(spriteDataStream, frame.x, frame.y, frame.w, frame.h, false);
+ assert((uint32)spriteDataStream->pos() == (currPos + frameSize));
+ }
+
_frames.push_back(frame);
}
+
+ if (_mode != 0) {
+ // Handle decompressing Fab encoded data
+ for (curFrame = 0; curFrame < _frameCount; curFrame++) {
+ FabDecompressor fab;
+
+ int srcSize = (curFrame == (_frameCount - 1)) ? spriteDataStream->size() - _frameOffsets[curFrame] :
+ _frameOffsets[curFrame + 1] - _frameOffsets[curFrame];
+ byte *srcData = (byte *)malloc(srcSize);
+ assert(srcData);
+ spriteDataStream->read(srcData, srcSize);
+
+ byte *destData = (byte *)malloc(frameSizes[curFrame]);
+ assert(destData);
+
+ fab.decompress(srcData, srcSize, destData, frameSizes[curFrame]);
+
+ // Load the frame
+ Common::MemoryReadStream *rs = new Common::MemoryReadStream(destData, frameSizes[curFrame]);
+ _frames[curFrame].frame = new M4Sprite(rs, _frames[curFrame].x, _frames[curFrame].y,
+ _frames[curFrame].w, _frames[curFrame].h, false);
+ delete rs;
+
+ free(srcData);
+ free(destData);
+ }
+ }
+
+
delete spriteStream;
delete spriteDataStream;
}
@@ -302,7 +366,12 @@ void SpriteAsset::loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndia
}
M4Sprite *SpriteAsset::getFrame(int frameIndex) {
- return _frames[frameIndex].frame;
+ if ((uint)frameIndex < _frames.size()) {
+ return _frames[frameIndex].frame;
+ } else {
+ warning("SpriteAsset::getFrame: Invalid frame %d, out of %d", frameIndex, _frames.size());
+ return _frames[_frames.size() - 1].frame;
+ }
}
void SpriteAsset::loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY) {
@@ -560,4 +629,23 @@ int32 AssetManager::getSpriteFrameCount(int32 hash) {
return _CELS[hash]->getCount();
}
+//--------------------------------------------------------------------------
+
+MadsSpriteSetCharInfo::MadsSpriteSetCharInfo(Common::SeekableReadStream *s) {
+ _totalFrames = s->readByte();
+ s->skip(1);
+ _numEntries = s->readUint16LE();
+
+ for (int i = 0; i < 16; ++i)
+ _frameList[i] = s->readUint16LE();
+ for (int i = 0; i < 16; ++i)
+ _frameList2[i] = s->readUint16LE();
+ for (int i = 0; i < 16; ++i)
+ _ticksList[i] = s->readUint16LE();
+
+ _unk1 = s->readUint16LE();
+ _ticksAmount = s->readByte();
+ _yScale = s->readByte();
+}
+
} // End of namespace M4
diff --git a/engines/m4/assets.h b/engines/m4/assets.h
index 7b0ce24dc4..3ae7fb2e22 100644
--- a/engines/m4/assets.h
+++ b/engines/m4/assets.h
@@ -44,12 +44,14 @@ namespace M4 {
#define CELS__PAL MKID_BE(' PAL') //' PAL'
#define CELS___SS MKID_BE(' SS') //' SS'
+#define SPRITE_SET_CHAR_INFO 4
+
class MadsM4Engine;
class Palette;
class BaseAsset {
public:
- BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
+ BaseAsset(MadsM4Engine *vm);
~BaseAsset();
const Common::String getName() const { return _name; }
protected:
@@ -100,12 +102,28 @@ struct SpriteAssetFrame {
M4Sprite *frame;
};
+class MadsSpriteSetCharInfo {
+public:
+ MadsSpriteSetCharInfo(Common::SeekableReadStream *s);
+
+ int _totalFrames;
+ int _numEntries;
+ int _frameList2[16];
+ int _frameList[16];
+ int _ticksList[16];
+ int _unk1;
+ int _ticksAmount;
+ int _yScale;
+};
+
class SpriteAsset : public BaseAsset {
public:
- SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false);
+ SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name,
+ bool asStream = false, int flags = 0);
+ SpriteAsset(MadsM4Engine *vm, const char *name);
~SpriteAsset();
void loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream);
- void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream);
+ void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int flags);
int32 getCount() { return _frameCount; }
int32 getFrameRate() const { return _frameRate; }
int32 getPixelSpeed() const { return _pixelSpeed; }
@@ -113,6 +131,7 @@ public:
int32 getFrameHeight(int index);
int32 getMaxFrameWidth() const { return _maxWidth; }
int32 getMaxFrameHeight() const { return _maxHeight; }
+ bool isBackground() const { return _isBackground; }
M4Sprite *getFrame(int frameIndex);
void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY);
RGB8* getPalette() { return _palette; }
@@ -122,7 +141,10 @@ public:
void translate(Palette *palette);
int32 getFrameSize(int index);
M4Sprite *operator[](int index) { return getFrame(index); }
+public:
+ MadsSpriteSetCharInfo *_charInfo;
protected:
+ Common::SeekableReadStream *_stream;
RGB8 _palette[256];
uint32 _colorCount;
uint32 _srcSize;
@@ -132,7 +154,11 @@ protected:
Common::Array<uint32> _frameOffsets;
Common::Array<SpriteAssetFrame> _frames;
uint32 _frameStartOffset;
- Common::SeekableReadStream *_stream;
+
+ // MADS sprite set fields
+ uint8 _mode;
+ bool _isBackground;
+
int32 parseSprite(bool isBigEndian = false);
void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false);
private:
diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp
index 0c2e80df0e..9a92cb04d1 100644
--- a/engines/m4/console.cpp
+++ b/engines/m4/console.cpp
@@ -104,8 +104,8 @@ bool Console::cmdListHotSpots(int argc, const char **argv) {
if (_vm->isM4()) {
DebugPrintf("Scene parallax\n");
_m4Vm->scene()->getSceneResources().parallax->dump();
- DebugPrintf("Scene props\n");
- _vm->_scene->getSceneResources().props->dump();
+ DebugPrintf("Scene dynamic hotspots\n");
+ _vm->_scene->getSceneResources().dynamicHotspots->dump();
}
return true;
}
@@ -197,7 +197,7 @@ bool Console::cmdShowSprite(int argc, const char **argv) {
if (y >= bg->height())
break;
- spr->copyTo(bg, x, y, (int)spr->getTransparentColor());
+ spr->copyTo(bg, x, y, (int)spr->getTransparencyIndex());
x += spr->width();
yMax = MAX(yMax, spr->height());
@@ -400,9 +400,9 @@ bool M4Console::cmdSceneInfo(int argc, const char **argv) {
DebugPrintf("Scene resources:\n");
DebugPrintf("artBase: %s\n", _m4Vm->scene()->getSceneResources().artBase);
DebugPrintf("pictureBase: %s\n", _m4Vm->scene()->getSceneResources().pictureBase);
- DebugPrintf("hotspotCount: %i\n", _m4Vm->scene()->getSceneResources().hotspotCount);
+ DebugPrintf("hotspotCount: %i\n", _m4Vm->scene()->getSceneResources().hotspots->size());
DebugPrintf("parallaxCount: %i\n", _m4Vm->scene()->getSceneResources().parallaxCount);
- DebugPrintf("propsCount: %i\n", _m4Vm->scene()->getSceneResources().propsCount);
+ DebugPrintf("dynHotspotCount: %i\n", _m4Vm->scene()->getSceneResources().dynamicHotspots->size());
DebugPrintf("frontY: %i\n", _m4Vm->scene()->getSceneResources().frontY);
DebugPrintf("backY: %i\n", _m4Vm->scene()->getSceneResources().backY);
DebugPrintf("frontScale: %i\n", _m4Vm->scene()->getSceneResources().frontScale);
diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp
index 746ced5d11..aab2fc95ce 100644
--- a/engines/m4/converse.cpp
+++ b/engines/m4/converse.cpp
@@ -858,8 +858,8 @@ void Converse::loadConversationMads(const char *convName) {
if (buffer[curPos - 1] == '\0') {
// end of string
//printf("%s\n", buffer);
- buf = new char[strlen(buffer)];
- sprintf(buf, "%s", buffer);
+ buf = new char[strlen(buffer) + 1];
+ strcpy(buf, buffer);
_convStrings.push_back(buf);
curPos = 0;
*buffer = 0;
diff --git a/engines/m4/detection.cpp b/engines/m4/detection.cpp
index 9493226c1a..4b204996f3 100644
--- a/engines/m4/detection.cpp
+++ b/engines/m4/detection.cpp
@@ -400,7 +400,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOMIDI
+ Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class M4MetaEngine : public AdvancedMetaEngine {
diff --git a/engines/m4/events.cpp b/engines/m4/events.cpp
index c0ca412f11..c66609844a 100644
--- a/engines/m4/events.cpp
+++ b/engines/m4/events.cpp
@@ -57,6 +57,10 @@ Events::Events(MadsM4Engine *vm) : _vm(vm) {
_console = new MadsConsole(_madsVm);
}
+Events::~Events() {
+ delete _console;
+}
+
M4EventType Events::handleEvents() {
static int oldX = -1, oldY = -1;
static uint32 dclickTime = 0;
@@ -252,7 +256,8 @@ bool Mouse::setCursorNum(int cursorIndex) {
_cursor = _cursorSprites->getFrame(cursorIndex);
// Set the cursor to the sprite
- CursorMan.replaceCursor((const byte *)_cursor->getBasePtr(), _cursor->width(), _cursor->height(), _cursor->xOffset, _cursor->yOffset, 0);
+ CursorMan.replaceCursor((const byte *)_cursor->getBasePtr(), _cursor->width(), _cursor->height(),
+ _cursor->xOffset, _cursor->yOffset, TRANSPARENT_COLOUR_INDEX);
return true;
}
diff --git a/engines/m4/events.h b/engines/m4/events.h
index 43b61c8f0d..1c1418d5f8 100644
--- a/engines/m4/events.h
+++ b/engines/m4/events.h
@@ -78,6 +78,7 @@ private:
public:
bool quitFlag;
Events(MadsM4Engine *vm);
+ virtual ~Events();
Common::Event &event() { return _event; }
Common::EventType type() { return _event.type; }
diff --git a/engines/m4/font.cpp b/engines/m4/font.cpp
index f8dec65412..b5965732e5 100644
--- a/engines/m4/font.cpp
+++ b/engines/m4/font.cpp
@@ -29,23 +29,48 @@
namespace M4 {
-Font::Font(MadsM4Engine *vm) : _vm(vm) {
+FontManager::~FontManager() {
+ for (uint i = 0; i < _entries.size(); ++i)
+ delete _entries[i];
+ _entries.clear();
+}
+
+Font *FontManager::getFont(const char *filename) {
+ // Append an extension if the filename doesn't already have one
+ char buffer[20];
+ strncpy(buffer, filename, 19);
+ if (!strchr(buffer, '.'))
+ strcat(buffer, ".ff");
+
+ // Check if the font is already loaded
+ for (uint i = 0; i < _entries.size(); ++i) {
+ if (!strcmp(_entries[i]->_filename, buffer))
+ return _entries[i];
+ }
+
+ Font *f = new Font(_vm, buffer);
+ _entries.push_back(f);
+ return f;
+}
+
+void FontManager::setFont(const char *filename) {
+ _currentFont = getFont(filename);
+}
+
+//--------------------------------------------------------------------------
+
+Font::Font(MadsM4Engine *vm, const char *filename) : _vm(vm) {
_sysFont = true;
- _filename = NULL;
+ strncpy(_filename, filename, 19);
+ _filename[19] = '\0';
+
//TODO: System font
_fontColors[0] = _vm->_palette->BLACK;
_fontColors[1] = _vm->_palette->WHITE;
_fontColors[2] = _vm->_palette->BLACK;
_fontColors[3] = _vm->_palette->DARK_GRAY;
-}
-
-void Font::setFont(const char *filename) {
- if ((_filename != NULL) && (strcmp(filename, _filename) == 0))
- // Already using specified font, so don't bother reloading
- return;
_sysFont = false;
- _filename = filename;
if (_vm->isM4())
setFontM4(filename);
@@ -134,20 +159,21 @@ Font::~Font() {
}
}
-void Font::setColor(uint8 color) {
+void Font::setColour(uint8 colour) {
if (_sysFont)
- _fontColors[1] = color;
+ _fontColors[1] = colour;
else
- _fontColors[3] = color;
+ _fontColors[3] = colour;
}
-void Font::setColors(uint8 alt1, uint8 alt2, uint8 foreground) {
+void Font::setColours(uint8 col1, uint8 col2, uint8 col3) {
if (_sysFont)
- _fontColors[1] = foreground;
+ _fontColors[1] = col3;
else {
- _fontColors[1] = alt1;
- _fontColors[2] = alt2;
- _fontColors[3] = foreground;
+ _fontColors[0] = 0xFF;
+ _fontColors[1] = col1;
+ _fontColors[2] = col2;
+ _fontColors[3] = col3;
}
}
diff --git a/engines/m4/font.h b/engines/m4/font.h
index e64f80b70d..19d15faa1e 100644
--- a/engines/m4/font.h
+++ b/engines/m4/font.h
@@ -59,19 +59,11 @@ namespace M4 {
class Font {
public:
- Font(MadsM4Engine *vm);
+ Font(MadsM4Engine *vm, const char *filename);
~Font();
- Font *getFont(const char *filename) {
- // TODO: Proper separation of font instances
- setFont(filename);
- return this;
- }
- void setFont(const char *filename);
- void setColor(uint8 color);
- void setColors(uint8 alt1, uint8 alt2, uint8 foreground);
- void setColour(uint8 colour) { setColor(colour); }
- void setColours(uint8 alt1, uint8 alt2, uint8 foreground) { setColors(alt1, alt2, foreground); }
+ void setColour(uint8 colour);
+ void setColours(uint8 col1, uint8 col2, uint8 col3);
int32 getWidth(const char *text, int spaceWidth = -1);
int32 getHeight() const { return _maxHeight; }
@@ -80,7 +72,8 @@ public:
int32 writeString(M4Surface *surface, const char *text, int x, int y, int width = 0, int spaceWidth = -1) {
return write(surface, text, x, y, width, spaceWidth, _fontColors);
}
-
+public:
+ char _filename[20];
private:
void setFontM4(const char *filename);
void setFontMads(const char *filename);
@@ -91,10 +84,39 @@ private:
uint16 *_charOffs;
uint8 *_charData;
bool _sysFont;
- const char *_filename;
uint8 _fontColors[4];
};
+class FontEntry {
+public:
+ Font *_font;
+
+ FontEntry() {
+ _font = NULL;
+ }
+ ~FontEntry() {
+ delete _font;
+ }
+};
+
+class FontManager {
+private:
+ MadsM4Engine *_vm;
+ Common::Array<Font *> _entries;
+ Font *_currentFont;
+public:
+ FontManager(MadsM4Engine *vm): _vm(vm) { _currentFont = NULL; }
+ ~FontManager();
+
+ Font *getFont(const char *filename);
+ void setFont(const char *filename);
+
+ Font *current() {
+ assert(_currentFont);
+ return _currentFont;
+ }
+};
+
} // End of namespace M4
#endif
diff --git a/engines/m4/globals.cpp b/engines/m4/globals.cpp
index 1768c71787..a96229a0b3 100644
--- a/engines/m4/globals.cpp
+++ b/engines/m4/globals.cpp
@@ -282,6 +282,9 @@ MadsGlobals::MadsGlobals(MadsEngine *vm): Globals(vm) {
playerSpriteChanged = false;
dialogType = DIALOG_NONE;
sceneNumber = -1;
+ for (int i = 0; i < 3; ++i)
+ actionNouns[i] = 0;
+ _difficultyLevel = 0;
}
MadsGlobals::~MadsGlobals() {
@@ -351,16 +354,16 @@ void MadsGlobals::loadMadsMessagesInfo() {
//printf("%i messages\n", count);
for (int i = 0; i < count; i++) {
- MessageItem *curMessage = new MessageItem();
- curMessage->id = messageS->readUint32LE();
- curMessage->offset = messageS->readUint32LE();
- curMessage->uncompSize = messageS->readUint16LE();
+ MessageItem curMessage;
+ curMessage.id = messageS->readUint32LE();
+ curMessage.offset = messageS->readUint32LE();
+ curMessage.uncompSize = messageS->readUint16LE();
if (i > 0)
- _madsMessages[i - 1]->compSize = curMessage->offset - _madsMessages[i - 1]->offset;
+ _madsMessages[i - 1].compSize = curMessage.offset - _madsMessages[i - 1].offset;
if (i == count - 1)
- curMessage->compSize = messageS->size() - curMessage->offset;
+ curMessage.compSize = messageS->size() - curMessage.offset;
//printf("id: %i, offset: %i, uncomp size: %i\n", curMessage->id, curMessage->offset, curMessage->uncompSize);
_madsMessages.push_back(curMessage);
@@ -382,7 +385,7 @@ void MadsGlobals::loadMadsObjects() {
int MadsGlobals::messageIndexOf(uint32 messageId) {
for (uint i = 0; i < _madsMessages.size(); ++i)
{
- if (_madsMessages[i]->id == messageId)
+ if (_madsMessages[i].id == messageId)
return i;
}
return -1;
@@ -395,15 +398,15 @@ const char *MadsGlobals::loadMessage(uint index) {
}
FabDecompressor fab;
- byte *compData = new byte[_madsMessages[index]->compSize];
- byte *buffer = new byte[_madsMessages[index]->uncompSize];
+ byte *compData = new byte[_madsMessages[index].compSize];
+ byte *buffer = new byte[_madsMessages[index].uncompSize];
Common::SeekableReadStream *messageS = _vm->res()->get("messages.dat");
- messageS->seek(_madsMessages[index]->offset, SEEK_SET);
- messageS->read(compData, _madsMessages[index]->compSize);
- fab.decompress(compData, _madsMessages[index]->compSize, buffer, _madsMessages[index]->uncompSize);
+ messageS->seek(_madsMessages[index].offset, SEEK_SET);
+ messageS->read(compData, _madsMessages[index].compSize);
+ fab.decompress(compData, _madsMessages[index].compSize, buffer, _madsMessages[index].uncompSize);
- for (int i = 0; i < _madsMessages[index]->uncompSize - 1; i++)
+ for (int i = 0; i < _madsMessages[index].uncompSize - 1; i++)
if (buffer[i] == '\0') buffer[i] = '\n';
_vm->res()->toss("messages.dat");
@@ -526,7 +529,9 @@ void MadsObject::load(Common::SeekableReadStream *stream) {
roomNumber = READ_LE_UINT16(&obj[2]);
article = (MADSArticles)obj[4];
vocabCount = obj[5] & 0x7f;
- assert(vocabCount <= 3);
+ // Phantom / Dragon
+ if (vocabCount > 3)
+ warning("MadsObject::load(), vocab cound > 3 (it's %d)", vocabCount);
for (int i = 0; i < vocabCount; ++i) {
vocabList[i].flags1 = obj[6 + i * 4];
diff --git a/engines/m4/globals.h b/engines/m4/globals.h
index a052a4c868..2bfedf1449 100644
--- a/engines/m4/globals.h
+++ b/engines/m4/globals.h
@@ -28,6 +28,7 @@
#include "common/scummsys.h"
#include "common/array.h"
+#include "common/hashmap.h"
#include "common/rect.h"
#include "common/file.h"
#include "common/list.h"
@@ -149,7 +150,7 @@ public:
void pauseGame(bool value);
};
-#define TOTAL_NUM_VARIABLES 256
+#define TOTAL_NUM_VARIABLES 210
#define PLAYER_INVENTORY 2
@@ -223,6 +224,13 @@ struct MadsConfigData {
int screenFades;
};
+#define GET_GLOBAL(x) (_madsVm->globals()->_globals[x])
+#define GET_GLOBAL32(x) (((uint32)_madsVm->globals()->_globals[x + 1] << 16) | _madsVm->globals()->_globals[x])
+#define SET_GLOBAL(x,y) _madsVm->globals()->_globals[x] = y
+#define SET_GLOBAL32(x,y) { _madsVm->globals()->_globals[x] = (y) & 0xffff; _madsVm->globals()->_globals[(x) + 1] = (y) >> 16; }
+
+typedef Common::HashMap<uint16, uint16> IntStorage;
+
class MadsGlobals : public Globals {
private:
struct MessageItem {
@@ -235,7 +243,7 @@ private:
MadsEngine *_vm;
Common::Array<char* > _madsVocab;
Common::Array<char* > _madsQuotes;
- Common::Array<MessageItem* > _madsMessages;
+ Common::Array<MessageItem> _madsMessages;
MadsObjectArray _madsObjects;
Common::List<int> _visitedScenes;
public:
@@ -243,12 +251,16 @@ public:
~MadsGlobals();
// MADS variables
- int _globals[TOTAL_NUM_VARIABLES];
+ uint16 _globals[TOTAL_NUM_VARIABLES];
MadsConfigData _config;
bool playerSpriteChanged;
MadsDialogType dialogType;
int sceneNumber;
int previousScene;
+ int16 _nextSceneId;
+ uint16 actionNouns[3];
+ IntStorage _dataMap;
+ int _difficultyLevel;
void loadMadsVocab();
uint32 getVocabSize() { return _madsVocab.size(); }
diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp
index 893c415576..423dda5e7e 100644
--- a/engines/m4/graphics.cpp
+++ b/engines/m4/graphics.cpp
@@ -65,10 +65,28 @@ void RGBList::setRange(int start, int count, const RGB8 *src) {
Common::copy(&src[0], &src[count], &_data[start]);
}
+/**
+ * Creates a duplicate of the given rgb list
+ */
+RGBList *RGBList::clone() const {
+ RGBList *dest = new RGBList(_size, _data, false);
+ _madsVm->_palette->addRange(dest);
+ return dest;
+}
+
//--------------------------------------------------------------------------
#define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2)
+M4Surface::~M4Surface() {
+ if (_rgbList) {
+ _madsVm->_palette->deleteRange(_rgbList);
+ delete _rgbList;
+ }
+ if (_ownsData)
+ free();
+}
+
void M4Surface::loadCodesM4(Common::SeekableReadStream *source) {
if (!source) {
free();
@@ -324,6 +342,16 @@ void M4Surface::clear() {
Common::set_to((byte *)pixels, (byte *)pixels + w * h, _vm->_palette->BLACK);
}
+void M4Surface::reset() {
+ ::free(pixels);
+ pixels = NULL;
+ if (_rgbList) {
+ _vm->_palette->deleteRange(_rgbList);
+ delete _rgbList;
+ _rgbList = NULL;
+ }
+}
+
void M4Surface::frameRect(const Common::Rect &r, uint8 color) {
Graphics::Surface::frameRect(r, color);
}
@@ -333,7 +361,7 @@ void M4Surface::fillRect(const Common::Rect &r, uint8 color) {
}
void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
- int transparentColor) {
+ int transparentColour) {
// Validation of the rectangle and position
if ((destX >= w) || (destY >= h))
return;
@@ -362,13 +390,13 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest
byte *destPtr = (byte *)pixels + (destY * width()) + destX;
for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
- if (transparentColor == -1)
+ if (transparentColour == -1)
// No transparency, so copy line over
Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr);
else {
// Copy each byte one at a time checking for the transparency color
for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr)
- if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr];
+ if (srcPtr[xCtr] != transparentColour) destPtr[xCtr] = srcPtr[xCtr];
}
srcPtr += src->width();
@@ -378,6 +406,174 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest
src->freeData();
}
+/**
+ * Copies a given image onto a destination surface with scaling, transferring only pixels that meet
+ * the specified depth requirement on a secondary surface contain depth information
+ */
+void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth,
+ M4Surface *depthsSurface, int scale, int transparentColour) {
+
+ if (scale == 100) {
+ // Copy the specified area
+ Common::Rect copyRect(0, 0, src->width(), src->height());
+
+ if (destX < 0) {
+ copyRect.left += -destX;
+ destX = 0;
+ } else if (destX + copyRect.width() > w) {
+ copyRect.right -= destX + copyRect.width() - w;
+ }
+ if (destY < 0) {
+ copyRect.top += -destY;
+ destY = 0;
+ } else if (destY + copyRect.height() > h) {
+ copyRect.bottom -= destY + copyRect.height() - h;
+ }
+
+ if (!copyRect.isValidRect())
+ return;
+
+ byte *data = src->getBasePtr();
+ byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left);
+ byte *depthsData = depthsSurface->getBasePtr();
+ byte *depthsPtr = depthsData + (depthsSurface->pitch * destY) + destX;
+ byte *destPtr = (byte *)pixels + (destY * pitch) + destX;
+
+ // 100% scaling variation
+ for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
+ // Copy each byte one at a time checking against the depth
+ for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) {
+ if ((depth <= (depthsPtr[xCtr] & 0x7f)) && (srcPtr[xCtr] != transparentColour))
+ destPtr[xCtr] = srcPtr[xCtr];
+ }
+
+ srcPtr += src->width();
+ depthsPtr += depthsSurface->width();
+ destPtr += width();
+ }
+
+ src->freeData();
+ depthsSurface->freeData();
+ return;
+ }
+
+ // Start of draw logic for scaled sprites
+ const byte *srcPixelsP = src->getBasePtr();
+
+ int destRight = this->width() - 1;
+ int destBottom = this->height() - 1;
+ bool normalFrame = true; // TODO: false for negative frame numbers
+ int frameWidth = src->width();
+ int frameHeight = src->height();
+
+ int highestDim = MAX(frameWidth, frameHeight);
+ bool lineDist[MADS_SURFACE_WIDTH];
+ int distIndex = 0;
+ int distXCount = 0, distYCount = 0;
+
+ int distCtr = 0;
+ do {
+ distCtr += scale;
+ if (distCtr < 100) {
+ lineDist[distIndex] = false;
+ } else {
+ lineDist[distIndex] = true;
+ distCtr -= 100;
+
+ if (distIndex < frameWidth)
+ ++distXCount;
+
+ if (distIndex < frameHeight)
+ ++distYCount;
+ }
+ } while (++distIndex < highestDim);
+
+ destX -= distXCount / 2;
+ destY -= distYCount - 1;
+
+ // Check x bounding area
+ int spriteLeft = 0;
+ int spriteWidth = distXCount;
+ int widthAmount = destX + distXCount - 1;
+
+ if (destX < 0) {
+ spriteWidth += destX;
+ spriteLeft -= destX;
+ }
+ widthAmount -= destRight;
+ if (widthAmount > 0)
+ spriteWidth -= widthAmount;
+
+ int spriteRight = spriteLeft + spriteWidth;
+ if (spriteWidth <= 0)
+ return;
+ if (!normalFrame) {
+ destX += distXCount - 1;
+ spriteLeft = -(distXCount - spriteRight);
+ spriteRight = (-spriteLeft + spriteWidth);
+ }
+
+ // Check y bounding area
+ int spriteTop = 0;
+ int spriteHeight = distYCount;
+ int heightAmount = destY + distYCount - 1;
+
+ if (destY < 0) {
+ spriteHeight += destY;
+ spriteTop -= destY;
+ }
+ heightAmount -= destBottom;
+ if (heightAmount > 0)
+ spriteHeight -= heightAmount;
+ int spriteBottom = spriteTop + spriteHeight;
+
+ if (spriteHeight <= 0)
+ return;
+
+ byte *destPixelsP = this->getBasePtr(destX + spriteLeft, destY + spriteTop);
+ const byte *depthPixelsP = depthsSurface->getBasePtr(destX + spriteLeft, destY + spriteTop);
+
+ spriteLeft = (spriteLeft * (normalFrame ? 1 : -1));
+
+ // Loop through the lines of the sprite
+ for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += src->pitch) {
+ if (!lineDist[yp])
+ // Not a display line, so skip it
+ continue;
+ // Check whether the sprite line is in the display range
+ ++sprY;
+ if ((sprY >= spriteBottom) || (sprY < spriteTop))
+ continue;
+
+ // Found a line to display. Loop through the pixels
+ const byte *srcP = srcPixelsP;
+ const byte *depthP = depthPixelsP;
+ byte *destP = destPixelsP;
+ for (int xp = 0, sprX = 0; xp < frameWidth; ++xp, ++srcP) {
+ if (xp < spriteLeft)
+ // Not yet reached start of display area
+ continue;
+ if (!lineDist[sprX++])
+ // Not a display pixel
+ continue;
+
+ if ((*srcP != transparentColour) && (depth <= (*depthP & 0x7f)))
+ *destP = *srcP;
+
+ ++destP;
+ ++depthP;
+ }
+
+ // Move to the next destination line
+ destPixelsP += this->pitch;
+ depthPixelsP += depthsSurface->pitch;
+ }
+
+ src->freeData();
+ depthsSurface->freeData();
+ this->freeData();
+}
+
void M4Surface::loadBackgroundRiddle(const char *sceneName) {
char resourceName[20];
Common::SeekableReadStream *stream;
@@ -389,17 +585,24 @@ void M4Surface::loadBackgroundRiddle(const char *sceneName) {
}
void M4Surface::loadBackground(int sceneNumber, RGBList **palData) {
- clear(); // clear previous scene
-
if (_vm->isM4() || (_vm->getGameType() == GType_RexNebular)) {
char resourceName[20];
Common::SeekableReadStream *stream;
if (_vm->getGameType() == GType_RexNebular) {
// Load Rex Nebular screen
+ bool hasPalette = palData != NULL;
+ if (!hasPalette)
+ palData = &_rgbList;
+
sprintf(resourceName, "rm%d.art", sceneNumber);
stream = _vm->_resourceManager->get(resourceName);
rexLoadBackground(stream, palData);
+
+ if (!hasPalette) {
+ _vm->_palette->addRange(_rgbList);
+ this->translate(_rgbList);
+ }
} else {
// Loads M4 game scene
if (palData)
@@ -542,16 +745,6 @@ void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList **
int sceneWidth = sourceUnc->readUint16LE();
int sceneHeight = sourceUnc->readUint16LE();
int sceneSize = sceneWidth * sceneHeight;
- if (sceneWidth > this->width()) {
- warning("Background width is %i, too large to fit in screen. Setting it to %i", sceneWidth, this->width());
- sceneWidth = this->width();
- sceneSize = sceneWidth * sceneHeight;
- }
- if (sceneHeight > this->height()) {
- warning("Background height is %i, too large to fit in screen.Setting it to %i", sceneHeight, this->height());
- sceneHeight = this->height();
- sceneSize = sceneWidth * sceneHeight;
- }
// Set palette
if (!palData) {
@@ -567,6 +760,7 @@ void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList **
sourceUnc = packData.getItemStream(1);
assert((int)sourceUnc->size() >= sceneSize);
+ create(sceneWidth, sceneHeight, 1);
byte *pData = (byte *)pixels;
sourceUnc->read(pData, sceneSize);
@@ -636,10 +830,8 @@ void M4Surface::m4LoadBackground(Common::SeekableReadStream *source) {
delete tileBuffer;
}
-void M4Surface::madsloadInterface(int index, RGBList **palData) {
- char resourceName[20];
- sprintf(resourceName, "i%d.int", index);
- MadsPack intFile(resourceName, _vm);
+void M4Surface::madsLoadInterface(const Common::String &filename) {
+ MadsPack intFile(filename.c_str(), _vm);
RGB8 *palette = new RGB8[16];
// Chunk 0, palette
@@ -653,7 +845,7 @@ void M4Surface::madsloadInterface(int index, RGBList **palData) {
intStream->readByte();
intStream->readByte();
}
- *palData = new RGBList(16, palette, true);
+ _rgbList = new RGBList(16, palette, true);
delete intStream;
// Chunk 1, data
@@ -661,22 +853,108 @@ void M4Surface::madsloadInterface(int index, RGBList **palData) {
create(320, 44, 1);
intStream->read(pixels, 320 * 44);
delete intStream;
+
+ // Translate the interface palette
+ _vm->_palette->addRange(_rgbList);
+ this->translate(_rgbList);
}
+void M4Surface::scrollX(int xAmount) {
+ if (xAmount == 0)
+ return;
+
+ byte buffer[80];
+ int direction = (xAmount > 0) ? -1 : 1;
+ int xSize = ABS(xAmount);
+ assert(xSize <= 80);
+
+ byte *srcP = (byte *)getBasePtr(0, 0);
+
+ for (int y = 0; y < height(); ++y, srcP += pitch) {
+ if (direction < 0) {
+ // Copy area to be overwritten
+ Common::copy(srcP, srcP + xSize, &buffer[0]);
+ // Shift the remainder of the line over the given area
+ Common::copy(srcP + xSize, srcP + width(), srcP);
+ // Move buffered area to the end of the line
+ Common::copy(&buffer[0], &buffer[xSize], srcP + width() - xSize);
+ } else {
+ // Copy area to be overwritten
+ Common::copy_backward(srcP + width() - xSize, srcP + width(), &buffer[80]);
+ // Shift the remainder of the line over the given area
+ Common::copy_backward(srcP, srcP + width() - xSize, srcP + width());
+ // Move buffered area to the start of the line
+ Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize);
+ }
+ }
+}
+
+void M4Surface::scrollY(int yAmount) {
+ if (yAmount == 0)
+ return;
+
+ int direction = (yAmount > 0) ? 1 : -1;
+ int ySize = ABS(yAmount);
+ assert(ySize < (height() / 2));
+ assert(width() == pitch);
+
+ int blockSize = ySize * width();
+ byte *tempData = (byte *)malloc(blockSize);
+ byte *pixelsP = (byte *)getBasePtr(0, 0);
+
+ if (direction > 0) {
+ // Buffer the lines to be overwritten
+ byte *srcP = (byte *)getBasePtr(0, height() - ySize);
+ Common::copy(srcP, srcP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy_backward(pixelsP, pixelsP + (pitch * (height() - ySize)),
+ pixelsP + (pitch * height()));
+ // Transfer the buffered lines top the top of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP);
+ } else {
+ // Buffer the lines to be overwritten
+ Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * height()), pixelsP);
+ // Transfer the buffered lines to the bottom of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (height() - ySize)));
+ }
+
+ ::free(tempData);
+}
+
+
void M4Surface::translate(RGBList *list, bool isTransparent) {
byte *p = getBasePtr(0, 0);
byte *palIndexes = list->palIndexes();
for (int i = 0; i < width() * height(); ++i, ++p) {
- if (!isTransparent || (*p != 0)) {
- assert(*p < list->size());
- *p = palIndexes[*p];
+ if (!isTransparent || (*p != TRANSPARENT_COLOUR_INDEX)) {
+ if (*p < list->size())
+ *p = palIndexes[*p];
+ else
+ warning("Pal index %d exceeds list size %d", *p, list->size());
}
}
freeData();
}
+M4Surface *M4Surface::flipHorizontal() const {
+ M4Surface *dest = new M4Surface(width(), height());
+ dest->_rgbList = (this->_rgbList == NULL) ? NULL : this->_rgbList->clone();
+
+ byte *destP = dest->getBasePtr();
+
+ for (int y = 0; y < height(); ++y) {
+ const byte *srcP = getBasePtr(width() - 1, y);
+ for (int x = 0; x < width(); ++x)
+ *destP++ = *srcP--;
+ }
+
+ return dest;
+}
+
//--------------------------------------------------------------------------
// Palette class
//
diff --git a/engines/m4/graphics.h b/engines/m4/graphics.h
index 4c89c50b8a..ecb5048b26 100644
--- a/engines/m4/graphics.h
+++ b/engines/m4/graphics.h
@@ -35,6 +35,13 @@
namespace M4 {
+#define MADS_SURFACE_WIDTH 320
+#define MADS_SURFACE_HEIGHT 156
+#define MADS_SCREEN_HEIGHT 200
+#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2)
+
+#define TRANSPARENT_COLOUR_INDEX 0xFF
+
struct BGR8 {
uint8 b, g, r;
};
@@ -68,6 +75,7 @@ public:
int size() { return _size; }
RGB8 &operator[](int idx) { return _data[idx]; }
void setRange(int start, int count, const RGB8 *src);
+ RGBList *clone() const;
};
// M4Surface
@@ -89,19 +97,36 @@ class M4Surface : protected Graphics::Surface {
private:
byte _color;
bool _isScreen;
+ RGBList *_rgbList;
+ bool _ownsData;
void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL);
void madsLoadBackground(int roomNumber, RGBList **palData = NULL);
void m4LoadBackground(Common::SeekableReadStream *source);
public:
M4Surface(bool isScreen = false) {
- create(g_system->getWidth(), g_system->getHeight(), 1);
+ create(g_system->getWidth(), isScreen ? g_system->getHeight() : MADS_SURFACE_HEIGHT, 1);
_isScreen = isScreen;
+ _rgbList = NULL;
+ _ownsData = true;
}
M4Surface(int width_, int height_) {
create(width_, height_, 1);
_isScreen = false;
+ _rgbList = NULL;
+ _ownsData = true;
}
+ M4Surface(int width_, int height_, byte *srcPixels, int pitch_) {
+ bytesPerPixel = 1;
+ w = width_;
+ h = height_;
+ pitch = pitch_;
+ pixels = srcPixels;
+ _rgbList = NULL;
+ _ownsData = false;
+ }
+
+ virtual ~M4Surface();
// loads a .COD file into the M4Surface
// TODO: maybe move this to the rail system? check where it makes sense
@@ -112,7 +137,8 @@ public:
// loads the specified background
void loadBackground(int sceneNumber, RGBList **palData = NULL);
void loadBackgroundRiddle(const char *sceneName);
- void madsloadInterface(int index, RGBList **palData);
+ void madsLoadInterface(int index, RGBList **palData = NULL);
+ void madsLoadInterface(const Common::String &filename);
void setColor(byte value) { _color = value; }
void setColour(byte value) { _color = value; }
@@ -131,6 +157,7 @@ public:
inline Common::Rect bounds() const { return Common::Rect(0, 0, width(), height()); }
inline int width() const { return w; }
inline int height() const { return h; }
+ inline int getPitch() const { return pitch; }
void setSize(int sizeX, int sizeY) { create(sizeX, sizeY, 1); }
inline byte *getBasePtr() {
return (byte *)pixels;
@@ -143,10 +170,12 @@ public:
}
void freeData();
void clear();
+ void reset();
void frameRect(const Common::Rect &r, uint8 color);
void fillRect(const Common::Rect &r, uint8 color);
- void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
- int transparentColor = -1);
+ void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY, int transparentColour = -1);
+ void copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthSurface,
+ int scale, int transparentColour = -1);
void update() {
if (_isScreen) {
@@ -156,18 +185,26 @@ public:
}
// copyTo methods
- inline void copyTo(M4Surface *dest, int transparentColor = -1) {
- dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColor);
+ inline void copyTo(M4Surface *dest, int transparentColour = -1) {
+ dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColour);
}
- inline void copyTo(M4Surface *dest, int x, int y, int transparentColor = -1) {
- dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColor);
+ inline void copyTo(M4Surface *dest, int x, int y, int transparentColour = -1) {
+ dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColour);
}
inline void copyTo(M4Surface *dest, const Common::Rect &srcBounds, int destX, int destY,
- int transparentColor = -1) {
- dest->copyFrom(this, srcBounds, destX, destY, transparentColor);
+ int transparentColour = -1) {
+ dest->copyFrom(this, srcBounds, destX, destY, transparentColour);
+ }
+ inline void copyTo(M4Surface *dest, int destX, int destY, int depth, M4Surface *depthsSurface, int scale,
+ int transparentColour = -1) {
+ dest->copyFrom(this, destX, destY, depth, depthsSurface, scale, transparentColour);
}
+ void scrollX(int xAmount);
+ void scrollY(int yAmount);
+
void translate(RGBList *list, bool isTransparent = false);
+ M4Surface *flipHorizontal() const;
};
enum FadeType {FT_TO_GREY, FT_TO_COLOR, FT_TO_BLOCK};
diff --git a/engines/m4/hotspot.cpp b/engines/m4/hotspot.cpp
index 9849cc7416..27180c5eb8 100644
--- a/engines/m4/hotspot.cpp
+++ b/engines/m4/hotspot.cpp
@@ -185,9 +185,9 @@ void HotSpotList::dump() {
uint32 HotSpotList::readHotSpotInteger(Common::SeekableReadStream* hotspotStream) {
if (_vm->isM4())
- return hotspotStream->readUint32LE();
+ return hotspotStream->readSint32LE();
else
- return hotspotStream->readUint16LE();
+ return hotspotStream->readSint16LE();
}
void HotSpotList::loadHotSpots(Common::SeekableReadStream* hotspotStream, int hotspotCount) {
@@ -196,7 +196,7 @@ void HotSpotList::loadHotSpots(Common::SeekableReadStream* hotspotStream, int ho
char buffer[256];
uint32 strLength = 0;
uint32 index = 0;
- uint32 feetX, feetY;
+ int feetX, feetY;
int cursorOffset = (_vm ->isM4()) ? 0 : 1;
for (int i = 0; i < hotspotCount; i++) {
@@ -206,6 +206,7 @@ void HotSpotList::loadHotSpots(Common::SeekableReadStream* hotspotStream, int ho
y2 = readHotSpotInteger(hotspotStream);
index = add(new HotSpot(x1, y1, x2, y2), i == 0);
currentHotSpot = get(index);
+ currentHotSpot->setIndex(index);
feetX = readHotSpotInteger(hotspotStream);
feetY = readHotSpotInteger(hotspotStream);
currentHotSpot->setFeet(feetX, feetY);
diff --git a/engines/m4/hotspot.h b/engines/m4/hotspot.h
index 5bb4f5888a..f650d5ff54 100644
--- a/engines/m4/hotspot.h
+++ b/engines/m4/hotspot.h
@@ -73,6 +73,8 @@ public:
int getFeetY() { return _feetY; }
int8 getArticle() const { return _articleNumber; }
Common::Rect getRect() const;
+ int getIndex() const { return _index; }
+ void setIndex(int index) { _index = index; }
int32 area() const { return (_rect.width() - 1) * (_rect.height() - 1); }
bool pointInside(int x, int y) { return _rect.contains(x, y); }
@@ -83,6 +85,7 @@ private:
bool _active;
int _feetX, _feetY;
uint8 _facing, _cursor;
+ int _index;
// Unused in Orion Burger, used in MADS games
uint8 _syntax;
@@ -101,6 +104,7 @@ public:
int add(HotSpot *hotspot, bool head = false);
HotSpot *get(int index) { return _hotspots[index]; }
HotSpot &operator[](int idx) { return *get(idx); }
+ int size() const { return _hotspots.size(); }
void remove(HotSpot *hotspot);
void unlinkItem(HotSpot *hotspot);
void clear();
diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp
index 897fb468cd..824896ad33 100644
--- a/engines/m4/m4.cpp
+++ b/engines/m4/m4.cpp
@@ -147,20 +147,24 @@ MadsM4Engine::~MadsM4Engine() {
delete _random;
delete _animation;
delete _palette;
+ delete _globals;
+ delete _sound;
+ delete _driver;
+ delete _resourceManager;
}
Common::Error MadsM4Engine::run() {
// Initialize backend
_screen = new M4Surface(true); // Special form for creating screen reference
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (native_mt32)
- driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
- _midi = new MidiPlayer(this, driver);
+ _midi = new MidiPlayer(this, _driver);
_midi->setGM(true);
_midi->setNativeMT32(native_mt32);
@@ -305,8 +309,6 @@ M4Engine::M4Engine(OSystem *syst, const M4GameDescription *gameDesc): MadsM4Engi
}
M4Engine::~M4Engine() {
- delete _resourceManager;
- delete _globals;
delete _converse;
}
@@ -502,8 +504,6 @@ MadsEngine::MadsEngine(OSystem *syst, const M4GameDescription *gameDesc): MadsM4
}
MadsEngine::~MadsEngine() {
- delete _globals;
- delete _resourceManager;
}
Common::Error MadsEngine::run() {
@@ -516,7 +516,6 @@ Common::Error MadsEngine::run() {
// Set up needed common functionality
MadsM4Engine::run();
- _scene = new MadsScene(this);
_palette->setMadsSystemPalette();
_mouse->init("cursor.ss", NULL);
@@ -540,16 +539,20 @@ Common::Error MadsEngine::run() {
//for (int i = 0; i < _globals->getMessagesSize(); i++)
//printf("%s\n----------\n", _globals->loadMessage(i));
- if ((getGameType() == GType_RexNebular) || (getGameType() == GType_DragonSphere)) {
- loadMenu(MAIN_MENU);
+ if (getGameType() == GType_RexNebular) {
+ MadsGameLogic::initialiseGlobals();
+ _scene = NULL;
+ loadMenu(MAIN_MENU);
} else {
- if (getGameType() == GType_DragonSphere) {
- _scene->loadScene(FIRST_SCENE);
- } else if (getGameType() == GType_Phantom) {
- //_scene->loadScene(FIRST_SCENE);
- _scene->loadScene(106); // a more interesting scene
- }
+ // Test code
+ _scene = new MadsScene(this);
+
+ startScene(FIRST_SCENE);
+ RGBList *_bgPalData;
+ _scene->loadBackground(FIRST_SCENE, &_bgPalData);
+ _palette->addRange(_bgPalData);
+ _scene->translate(_bgPalData);
_scene->show();
@@ -568,15 +571,6 @@ Common::Error MadsEngine::run() {
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_KP_MULTIPLY, &textviewHotkeyHandler);
- // Load the general game SFX/voices
- if (getGameType() == GType_RexNebular) {
- _sound->loadDSRFile("rex009.dsr");
- } else if (getGameType() == GType_Phantom) {
- _sound->loadDSRFile("phan009.dsr");
- } else if (getGameType() == GType_DragonSphere) {
- _sound->loadDSRFile("drag009.dsr");
- }
-
uint32 nextFrame = g_system->getMillis();
while (!_events->quitFlag) {
eventHandler();
diff --git a/engines/m4/m4.h b/engines/m4/m4.h
index 1f34bd3685..9e3035957a 100644
--- a/engines/m4/m4.h
+++ b/engines/m4/m4.h
@@ -29,6 +29,7 @@
#include "common/scummsys.h"
#include "common/util.h"
#include "common/random.h"
+#include "sound/mididrv.h"
#include "engines/engine.h"
@@ -41,6 +42,7 @@
#include "m4/events.h"
#include "m4/font.h"
#include "m4/scene.h"
+#include "m4/mads_player.h"
#include "m4/mads_scene.h"
#include "m4/m4_scene.h"
#include "m4/actor.h"
@@ -123,7 +125,7 @@ enum {
struct M4GameDescription;
-#define GAME_FRAME_DELAY 50
+#define GAME_FRAME_DELAY 20
#define VALIDATE_MADS assert(!_vm->isM4())
@@ -144,6 +146,7 @@ protected:
void shutdown();
+ MidiDriver *_driver;
MidiPlayer *_midi;
public:
@@ -212,6 +215,7 @@ private:
public:
MadsConversation _converse;
uint32 _currentTimer;
+ MadsPlayer _player;
public:
MadsEngine(OSystem *syst, const M4GameDescription *gameDesc);
virtual ~MadsEngine();
@@ -220,6 +224,12 @@ public:
MadsGlobals *globals() { return (MadsGlobals *)_globals; }
MadsScene *scene() { return (MadsScene *)_scene; }
+ void startScene(int sceneNum) {
+ if (!_scene)
+ _scene = new MadsScene(this);
+ _scene->show();
+ _scene->loadScene(101);
+ }
};
class M4Engine : public MadsM4Engine {
diff --git a/engines/m4/m4_scene.cpp b/engines/m4/m4_scene.cpp
index 79122a9564..475fdba653 100644
--- a/engines/m4/m4_scene.cpp
+++ b/engines/m4/m4_scene.cpp
@@ -45,7 +45,7 @@ M4Scene::M4Scene(M4Engine *vm): _sceneResources(), Scene(vm, &_sceneResources) {
_sceneResources.hotspots = new HotSpotList();
_sceneResources.parallax = new HotSpotList();
- _sceneResources.props = new HotSpotList();
+ _sceneResources.dynamicHotspots = new HotSpotList();
_interfaceSurface = new M4InterfaceView(vm);
}
@@ -74,9 +74,9 @@ void M4Scene::loadSceneResources(int sceneNumber) {
if (sceneS != NULL) {
sceneS->read(_sceneResources.artBase, MAX_CHK_FILENAME_SIZE);
sceneS->read(_sceneResources.pictureBase, MAX_CHK_FILENAME_SIZE);
- _sceneResources.hotspotCount = sceneS->readUint32LE();
+ int hotspotCount = sceneS->readUint32LE();
_sceneResources.parallaxCount = sceneS->readUint32LE();
- _sceneResources.propsCount = sceneS->readUint32LE();
+ int dynHotspotCount = sceneS->readUint32LE();
_sceneResources.frontY = sceneS->readUint32LE();
_sceneResources.backY = sceneS->readUint32LE();
_sceneResources.frontScale = sceneS->readUint32LE();
@@ -99,11 +99,11 @@ void M4Scene::loadSceneResources(int sceneNumber) {
// Clear current hotspot lists
_sceneResources.hotspots->clear();
_sceneResources.parallax->clear();
- _sceneResources.props->clear();
+ _sceneResources.dynamicHotspots->clear();
- _sceneResources.hotspots->loadHotSpots(sceneS, _sceneResources.hotspotCount);
+ _sceneResources.hotspots->loadHotSpots(sceneS, hotspotCount);
_sceneResources.parallax->loadHotSpots(sceneS, _sceneResources.parallaxCount);
- _sceneResources.props->loadHotSpots(sceneS, _sceneResources.propsCount);
+ _sceneResources.dynamicHotspots->loadHotSpots(sceneS, dynHotspotCount);
// Note that toss() deletes the MemoryReadStream
_vm->res()->toss(filename);
@@ -207,7 +207,7 @@ void M4Scene::leaveScene() {
Scene::leaveScene();
}
-void M4Scene::checkHotspotAtMousePos(int x, int y) {
+void M4Scene::mouseMove(int x, int y) {
if (_vm->getGameType() == GType_Riddle)
return;
diff --git a/engines/m4/m4_scene.h b/engines/m4/m4_scene.h
index 329582caf4..2216779a3e 100644
--- a/engines/m4/m4_scene.h
+++ b/engines/m4/m4_scene.h
@@ -69,7 +69,7 @@ public:
virtual void leaveScene();
virtual void loadSceneCodes(int sceneNumber, int index = 0);
virtual void show();
- virtual void checkHotspotAtMousePos(int x, int y);
+ virtual void mouseMove(int x, int y);
virtual void leftClick(int x, int y);
virtual void rightClick(int x, int y);
virtual void update();
diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp
index 954916700c..5e19e90533 100644
--- a/engines/m4/mads_anim.cpp
+++ b/engines/m4/mads_anim.cpp
@@ -441,7 +441,12 @@ void TextviewView::processText() {
AnimviewView::AnimviewView(MadsM4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())),
- _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT) {
+ MadsView(this), _backgroundSurface(MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT),
+ _codeSurface(MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT) {
+
+ MadsView::_bgSurface = &_backgroundSurface;
+ MadsView::_depthSurface = &_codeSurface;
+ MadsView::setViewport(Common::Rect(0, MADS_Y_OFFSET, MADS_SURFACE_WIDTH, MADS_Y_OFFSET + MADS_SURFACE_HEIGHT));
_screenType = VIEWID_ANIMVIEW;
_screenFlags.layer = LAYER_BACKGROUND;
@@ -452,27 +457,36 @@ AnimviewView::AnimviewView(MadsM4Engine *vm):
_palData = NULL;
_previousUpdate = 0;
_transition = kTransitionNone;
+ _activeAnimation = NULL;
+ _bgLoadFlag = true;
+ _startFrame = -1;
+ _scriptDone = false;
+
reset();
// Set up system palette colors
_vm->_palette->setMadsSystemPalette();
+ // Block reserved palette ranges
+ _vm->_palette->blockRange(16, 2);
+ _vm->_palette->blockRange(250, 4);
+
clear();
- _bgSurface.clear();
+ _backgroundSurface.clear();
- int y = (height() - MADS_SURFACE_HEIGHT) / 2;
setColor(2);
- hLine(0, width() - 1, y - 2);
- hLine(0, width() - 1, height() - y + 1);
+ hLine(0, width() - 1, MADS_Y_OFFSET - 2);
+ hLine(0, width() - 1, MADS_Y_OFFSET + MADS_SURFACE_HEIGHT + 2);
}
AnimviewView::~AnimviewView() {
if (_script)
_vm->res()->toss(_resourceName);
+ delete _activeAnimation;
}
void AnimviewView::reset() {
- _bgSurface.clear();
+ _backgroundSurface.clear();
_soundDriverLoaded = false;
}
@@ -504,34 +518,51 @@ bool AnimviewView::onEvent(M4EventType eventType, int32 param, int x, int y, boo
}
void AnimviewView::updateState() {
- if (!_script)
- return;
+ MadsView::update();
- // Only update state if wait period has expired
- if (_previousUpdate > 0) {
- if (g_system->getMillis() - _previousUpdate < 100)
- return;
+ if (!_script || _scriptDone)
+ return;
- _previousUpdate = g_system->getMillis();
+ if (!_activeAnimation) {
+ readNextCommand();
+ assert(_activeAnimation);
}
- // Check if we're ready for the next command
- bool animRunning = false;
- if (!animRunning) {
- if (_script->eos() || _script->err()) {
- scriptDone();
- return;
- }
+ // Update the current animation
+ _activeAnimation->update();
+ if (_activeAnimation->freeFlag()) {
+ delete _activeAnimation;
+ _activeAnimation = NULL;
+
+ // Clear up current background and sprites
+ _backgroundSurface.reset();
+ clearLists();
+
+ // Reset flags
+ _startFrame = -1;
readNextCommand();
- // FIXME: Replace flag with proper animation end check
- animRunning = true;
+ // Check if script is finished
+ if (_scriptDone) {
+ scriptDone();
+ return;
+ }
}
+
+ refresh();
}
void AnimviewView::readNextCommand() {
+static bool tempFlag = true;//****DEBUG - Temporarily allow me to skip several intro scenes ****
+
while (!_script->eos() && !_script->err()) {
+ if (!tempFlag) {
+ tempFlag = true;
+ strncpy(_currentLine, _script->readLine().c_str(), 79);
+ strncpy(_currentLine, _script->readLine().c_str(), 79);
+ }
+
strncpy(_currentLine, _script->readLine().c_str(), 79);
// Process any switches on the line
@@ -559,49 +590,27 @@ void AnimviewView::readNextCommand() {
break;
}
- if (strchr(_currentLine, '.') == NULL)
- strcat(_currentLine, ".aa");
-
- AAFile aaFile(_currentLine, _vm);
-
- // Initial validation
- if (aaFile.flags & AA_HAS_FONT) {
- assert(_vm->_resourceManager->resourceExists(aaFile.fontResource.c_str()));
- }
-
- for (int seriesCtr = 0; seriesCtr < aaFile.seriesCount; ++seriesCtr)
- assert(_vm->_resourceManager->resourceExists(aaFile.filenames[seriesCtr].c_str()));
-
- // Start sound
- if (aaFile.flags & AA_HAS_SOUND) {
- char buffer[100];
- strcpy(buffer, aaFile.soundName.c_str());
- buffer[0] = 'A'; // A for AdLib resource
-
- /*Common::SeekableReadStream *stream = */_vm->_resourceManager->get(buffer);
-
- _vm->_resourceManager->toss(buffer);
+ if (!_currentLine[0]) {
+ // A blank line at this point means that the end of the animation has been reached
+ _scriptDone = true;
+ return;
}
+ if (strchr(_currentLine, '.') == NULL)
+ strcat(_currentLine, ".aa");
- char artFile[80];
- sprintf(artFile, "rm%d.art", aaFile.roomNumber);
+ uint16 flags = 0;
+ if (_bgLoadFlag)
+ flags |= 0x100;
- // Not all scenes have a background. If there is one, refresh it
- if (_vm->_resourceManager->resourceExists(artFile)) {
- if (_palData) {
- _vm->_palette->deleteRange(_palData);
- delete _palData;
- }
- _bgSurface.loadBackground(aaFile.roomNumber, &_palData);
- _vm->_palette->addRange(_palData);
- _bgSurface.translate(_palData);
- }
+ _activeAnimation = new MadsAnimation(_vm, this);
+ _activeAnimation->initialise(_currentLine, flags, &_backgroundSurface, &_codeSurface);
- // Grab what the final palete will be
- RGB8 destPalette[256];
- _vm->_palette->grabPalette(destPalette, 0, 256);
+ if (_startFrame != -1)
+ _activeAnimation->setCurrentFrame(_startFrame);
+ _spriteSlots.fullRefresh();
+/*
// Handle scene transition
switch (_transition) {
case kTransitionNone:
@@ -631,16 +640,14 @@ void AnimviewView::readNextCommand() {
// nothing to do
break;
}
-
- // Refresh the view
- int yp = (height() - _bgSurface.height()) / 2;
- _bgSurface.copyTo(this, 0, yp);
+*/
_vm->_resourceManager->toss(_currentLine);
}
void AnimviewView::scriptDone() {
+return;
AnimviewCallback fn = _callback;
MadsM4Engine *vm = _vm;
@@ -655,6 +662,7 @@ void AnimviewView::scriptDone() {
Switches are: (taken from the help of the original executable)
-b Toggle background load status off/on.
-c:char Specify sound card id letter.
+ -f:num Specify a specific starting frame number
-g Stay in graphics mode on exit.
-h[:ex] Disable EMS/XMS high memory support.
-i Switch sound interrupts mode off/on.
@@ -698,61 +706,41 @@ void AnimviewView::processCommand() {
str_upper(commandStr);
char *param = commandStr;
- if (!strncmp(commandStr, "X", 1)) {
- //printf("X ");
- } else if (!strncmp(commandStr, "W", 1)) {
- //printf("W ");
- } else if (!strncmp(commandStr, "R", 1)) {
- param = param + 2;
- //printf("R:%s ", param);
- } else if (!strncmp(commandStr, "O", 1)) {
+ switch (commandStr[0]) {
+ case 'B':
+ // Toggle background load flag
+ _bgLoadFlag = !_bgLoadFlag;
+ break;
+
+ case 'F':
+ // Start animation at a specific frame
+ ++param;
+ assert(*param == ':');
+ _startFrame = atoi(++param);
+ break;
+
+ case 'O':
param = param + 2;
//printf("O:%i ", atoi(param));
_transition = atoi(param);
- } else {
- error("Unknown response command: '%s'", commandStr);
- }
-}
+ break;
-AAFile::AAFile(const char *resourceName, MadsM4Engine* vm): MadsPack(resourceName, vm) {
- Common::MemoryReadStream stream1(*getItemStream(1));
- Common::MemoryReadStream stream2(*getItemStream(2));
-
- Common::MemoryReadStream stream(*getItemStream(0));
-
- seriesCount = stream.readUint16LE();
- frameCount = stream.readUint16LE();
- frameEntryCount = stream.readUint16LE();
- stream.skip(3);
- flags = stream.readByte();
- stream.skip(4);
- roomNumber = stream.readUint16LE();
- stream.skip(10);
- frameTicks = stream.readUint16LE();
-
- stream.skip(21);
- for (int i = 0; i < 10; ++i) {
- char filename[13];
- stream.read(filename, 13);
- filenames.push_back(Common::String(filename, 13));
- }
-
- stream.skip(81);
- char name[100];
- stream.read(name, 13);
- lbmFilename = Common::String(name, 13);
+ case 'R':
+ param = param + 2;
+ //printf("R:%s ", param);
+ break;
- stream.skip(365);
- stream.read(name, 13);
- spritesFilename = Common::String(name, 13);
+ case 'W':
+ //printf("W ");
+ break;
- stream.skip(48);
- stream.read(name, 13);
- soundName = Common::String(name, 13);
+ case 'X':
+ //printf("X ");
+ break;
- stream.skip(26);
- stream.read(name, 14);
- fontResource = Common::String(name, 14);
+ default:
+ error("Unknown response command: '%s'", commandStr);
+ }
}
}
diff --git a/engines/m4/mads_anim.h b/engines/m4/mads_anim.h
index 680c5ff901..b33ea24071 100644
--- a/engines/m4/mads_anim.h
+++ b/engines/m4/mads_anim.h
@@ -28,24 +28,12 @@
#include "m4/viewmgr.h"
#include "m4/compression.h"
+#include "m4/animation.h"
#include "common/str-array.h"
namespace M4 {
-enum SceneTransition {
- kTransitionNone = 0,
- kTransitionFadeIn = 1,
- kTransitionFadeIn2 = 2,
- kTransitionBoxInBottomLeft = 3,
- kTransitionBoxInBottomRight = 4,
- kTransitionBoxInTopLeft = 5,
- kTransitionBoxInTopRight = 6,
- kTransitionPanLeftToRight = 7,
- kTransitionPanRightToLeft = 8,
- kTransitionCircleIn = 9
-};
-
typedef void (*TextviewCallback)(MadsM4Engine *vm);
class TextviewView : public View {
@@ -89,36 +77,22 @@ public:
typedef void (*AnimviewCallback)(MadsM4Engine *vm);
-class AAFile : public MadsPack {
-public:
- AAFile(const char *resourceName, MadsM4Engine* vm);
-
- uint16 seriesCount;
- uint16 frameCount;
- uint16 frameEntryCount;
- uint8 flags;
- uint16 roomNumber;
- uint16 frameTicks;
- Common::StringArray filenames;
- Common::String lbmFilename;
- Common::String spritesFilename;
- Common::String soundName;
- Common::String fontResource;
-};
-
-enum AAFlags {AA_HAS_FONT = 0x20, AA_HAS_SOUND = 0x8000};
-
-class AnimviewView : public View {
+class AnimviewView : public View, MadsView {
private:
char _resourceName[80];
Common::SeekableReadStream *_script;
+ bool _scriptDone;
uint32 _previousUpdate;
char _currentLine[80];
- M4Surface _bgSurface;
+ M4Surface _backgroundSurface;
+ M4Surface _codeSurface;
AnimviewCallback _callback;
bool _soundDriverLoaded;
RGBList *_palData;
int _transition;
+ MadsAnimation *_activeAnimation;
+ bool _bgLoadFlag;
+ int _startFrame;
void reset();
void readNextCommand();
diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp
index d0b7021f38..e7c20b237d 100644
--- a/engines/m4/mads_logic.cpp
+++ b/engines/m4/mads_logic.cpp
@@ -29,6 +29,114 @@
namespace M4 {
+void MadsGameLogic::initialiseGlobals() {
+ // Clear the entire globals list
+ Common::set_to(&_madsVm->globals()->_globals[0], &_madsVm->globals()->_globals[TOTAL_NUM_VARIABLES], 0);
+
+ SET_GLOBAL(4, 8);
+ SET_GLOBAL(33, 1);
+ SET_GLOBAL(10, 0xFFFF);
+ SET_GLOBAL(13, 0xFFFF);
+ SET_GLOBAL(15, 0xFFFF);
+ SET_GLOBAL(19, 0xFFFF);
+ SET_GLOBAL(20, 0xFFFF);
+ SET_GLOBAL(21, 0xFFFF);
+ SET_GLOBAL(95, 0xFFFF);
+
+ // TODO: unknown sub call
+
+ // Put the values 0 through 3 in a random ordering in global slots 83 - 86
+ for (int idx = 0; idx < 4; ) {
+ int randVal = _madsVm->_random->getRandomNumber(4);
+ SET_GLOBAL(83 + idx, randVal);
+
+ // Check whether the given value has already been used
+ bool flag = false;
+ for (int idx2 = 0; idx2 < idx; ++idx2) {
+ if (randVal == GET_GLOBAL(83 + idx2))
+ flag = true;
+ }
+
+ if (!flag)
+ ++idx;
+ }
+
+ // Put the values 0 through 3 in a random ordering in global slots 87 - 90
+ for (int idx = 0; idx < 4; ) {
+ int randVal = _madsVm->_random->getRandomNumber(3);
+ SET_GLOBAL(87 + idx, randVal);
+
+ // Check whether the given value has already been used
+ bool flag = false;
+ for (int idx2 = 0; idx2 < idx; ++idx2) {
+ if (randVal == GET_GLOBAL(87 + idx2))
+ flag = true;
+ }
+
+ if (!flag)
+ ++idx;
+ }
+
+ // Miscellaneous global settings
+ SET_GLOBAL(120, 501);
+ SET_GLOBAL(121, 0xFFFF);
+ SET_GLOBAL(110, 0xFFFF);
+ SET_GLOBAL(119, 1);
+ SET_GLOBAL(134, 4);
+ SET_GLOBAL(190, 201);
+ SET_GLOBAL(191, 301);
+ SET_GLOBAL(192, 413);
+ SET_GLOBAL(193, 706);
+ SET_GLOBAL(194, 801);
+ SET_GLOBAL(195, 551);
+ SET_GLOBAL(196, 752);
+
+ // Fill out the globals 200 - 209 with unique random number values less than 10000
+ for (int idx = 0; idx < 10; ) {
+ int randVal = _madsVm->_random->getRandomNumber(9999);
+ SET_GLOBAL(200 + idx, randVal);
+
+ // Check whether the given value has already been used
+ bool flag = false;
+ for (int idx2 = 0; idx2 < idx; ++idx2) {
+ if (randVal == GET_GLOBAL(87 + idx2))
+ flag = true;
+ }
+
+ if (!flag)
+ ++idx;
+ }
+
+ switch (_madsVm->globals()->_difficultyLevel) {
+ case 1:
+ // Very hard
+ SET_GLOBAL(35, 0);
+ // TODO: object set room
+ SET_GLOBAL(137, 5);
+ SET_GLOBAL(136, 0);
+ break;
+
+ case 2:
+ // Hard
+ SET_GLOBAL(35, 0);
+ // TODO: object set room
+ SET_GLOBAL(136, 0xFFFF);
+ SET_GLOBAL(137, 6);
+ break;
+
+ case 3:
+ // Easy
+ SET_GLOBAL(35, 2);
+ // TODO: object set room
+ break;
+ }
+
+ _madsVm->_player._direction = 8;
+ _madsVm->_player._newDirection = 8;
+
+ // TODO: unknown processing routine getting called for 'RXM' and 'ROX'
+}
+
/*--------------------------------------------------------------------------*/
const char *MadsSceneLogic::formAnimName(char sepChar, int16 suffixNum) {
@@ -54,13 +162,13 @@ void MadsSceneLogic::getSceneSpriteSet() {
strcpy(prefix, "");
_madsVm->globals()->playerSpriteChanged = true;
- _madsVm->scene()->loadPlayerSprites(prefix);
+ _madsVm->_player.loadSprites(prefix);
// if ((_sceneNumber == 105) ((_sceneNumber == 109) && (word_84800 != 0)))
// _madsVm->globals()->playerSpriteChanged = true;
-// _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF);
-// _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4);
+ _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF);
+ _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4);
}
void MadsSceneLogic::getAnimName() {
@@ -69,6 +177,10 @@ void MadsSceneLogic::getAnimName() {
strcpy(_madsVm->scene()->_aaName, newName);
}
+IntStorage &MadsSceneLogic::dataMap() {
+ return _madsVm->globals()->_dataMap;
+}
+
/*--------------------------------------------------------------------------*/
uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) {
@@ -77,32 +189,31 @@ uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) {
return _madsVm->scene()->loadSceneSpriteSet(resName);
}
-uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
- M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1);
+uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
+ M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0);
uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2),
spriteFrame->y + (spriteFrame->height() / 2)));
- return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
- -1, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0);
+ return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, flipped, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
+ true, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0);
}
-uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
- M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1);
-warning("%d %dx%d %d/%d", srcSpriteIdx, spriteFrame->x, spriteFrame->y, spriteFrame->width(), spriteFrame->height());
+uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
+ M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0);
uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2),
spriteFrame->y + (spriteFrame->height() / 2)));
- return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
- -1, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0);
+ return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, flipped, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
+ true, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0);
}
-uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
- M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1);
+uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
+ M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0);
uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2),
spriteFrame->y + (spriteFrame->height() / 2)));
- return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
- -1, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0);
+ return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, flipped, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
+ true, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0);
}
void MadsSceneLogic::activateHotspot(int idx, bool active) {
@@ -147,6 +258,60 @@ void MadsSceneLogic::lowRoomsEntrySound() {
}
}
+void MadsSceneLogic::getPlayerSpritesPrefix() {
+ _madsVm->_sound->playSound(5);
+
+ char oldName[80];
+ strcpy(oldName, _madsVm->_player._spritesPrefix);
+
+ if ((_madsVm->globals()->_nextSceneId <= 103) || (_madsVm->globals()->_nextSceneId == 111))
+ strcpy(_madsVm->_player._spritesPrefix, (_madsVm->globals()->_globals[0] == SEX_FEMALE) ? "ROX" : "RXM");
+ else if (_madsVm->globals()->_nextSceneId <= 110)
+ strcpy(_madsVm->_player._spritesPrefix, "RXSM");
+ else if (_madsVm->globals()->_nextSceneId == 112)
+ strcpy(_madsVm->_player._spritesPrefix, "");
+
+ if (strcmp(oldName, _madsVm->_player._spritesPrefix) != 0)
+ _madsVm->_player._spritesChanged = true;
+
+ if ((_madsVm->globals()->_nextSceneId == 105) ||
+ ((_madsVm->globals()->_nextSceneId == 109) && (_madsVm->globals()->_globals[15] != 0))) {
+ // TODO: unknown flag setting
+ _madsVm->_player._spritesChanged = true;
+ }
+
+ _madsVm->_palette->setEntry(16, 40, 255, 255);
+ _madsVm->_palette->setEntry(17, 40, 180, 180);
+
+}
+
+void MadsSceneLogic::getPlayerSpritesPrefix2() {
+ _madsVm->_sound->playSound(5);
+
+ char oldName[80];
+ strcpy(oldName, _madsVm->_player._spritesPrefix);
+
+ if ((_madsVm->globals()->_nextSceneId == 213) || (_madsVm->globals()->_nextSceneId == 216))
+ strcpy(_madsVm->_player._spritesPrefix, "");
+ else if (_madsVm->globals()->_globals[0] == SEX_MALE)
+ strcpy(_madsVm->_player._spritesPrefix, "RXM");
+ else
+ strcpy(_madsVm->_player._spritesPrefix, "ROX");
+
+ // TODO: unknown flag setting for next scene Id > 212
+
+ if (strcmp(oldName, _madsVm->_player._spritesPrefix) != 0)
+ _madsVm->_player._spritesChanged = true;
+
+/* if ((_madsVm->globals()->_nextSceneId == 203) && (_madsVm->globals()->_nextSceneId == 204) &&
+ (_madsVm->globals()->_globals[0x22] == 0))
+ // TODO: unknown flag set
+*/
+ _madsVm->_palette->setEntry(16, 40, 255, 255);
+ _madsVm->_palette->setEntry(17, 40, 180, 180);
+}
+
+
/*--------------------------------------------------------------------------*/
/**
@@ -160,7 +325,7 @@ void MadsSceneLogic::selectScene(int sceneNum) {
assert(sceneNum == 101);
_sceneNumber = sceneNum;
-
+ Common::set_to(&_spriteIndexes[0], &_spriteIndexes[50], 0);
}
void MadsSceneLogic::setupScene() {
@@ -170,7 +335,11 @@ void MadsSceneLogic::setupScene() {
// sub_1e754(animName, 3);
- getSceneSpriteSet();
+ if ((_sceneNumber >= 101) && (_sceneNumber <= 112))
+ getPlayerSpritesPrefix();
+ else
+ getPlayerSpritesPrefix2();
+
getAnimName();
}
@@ -185,20 +354,18 @@ void MadsSceneLogic::enterScene() {
_spriteIndexes[12] = loadSpriteSet(8, 'x');
_spriteIndexes[13] = loadSpriteSet(0, 'x');
- _spriteIndexes[15] = startCycledSpriteSequence(_spriteIndexes[0], 0, 5, 0, 0, 25);
-
- _spriteIndexes[16] = startCycledSpriteSequence(_spriteIndexes[1], 0, 4, 0, 1, 0);
- _spriteIndexes[17] = startCycledSpriteSequence(_spriteIndexes[2], 0, 4, 0, 1, 0);
-
-// _madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70);
+ _spriteIndexes[15] = startCycledSpriteSequence(_spriteIndexes[0], false, 5, 0, 0, 25);
+ _spriteIndexes[16] = startCycledSpriteSequence(_spriteIndexes[1], false, 4, 0, 1, 0);
+ _spriteIndexes[17] = startCycledSpriteSequence(_spriteIndexes[2], false, 4, 0, 1, 0);
- _spriteIndexes[18] = startReversibleSpriteSequence(_spriteIndexes[3], 0, 10, 0, 0, 60);
- _spriteIndexes[19] = startCycledSpriteSequence(_spriteIndexes[4], 0, 5, 0, 1, 0);
- _spriteIndexes[20] = startCycledSpriteSequence(_spriteIndexes[5], 0, 10, 0, 2, 0);
- _spriteIndexes[21] = startCycledSpriteSequence(_spriteIndexes[6], 0, 6, 0, 0, 0);
+ _madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70);
- _spriteIndexes[23] = startCycledSpriteSequence(_spriteIndexes[8], 0, 6, 0, 10, 4);
- _spriteIndexes[24] = startCycledSpriteSequence(_spriteIndexes[9], 0, 6, 0, 32, 47);
+ _spriteIndexes[18] = startReversibleSpriteSequence(_spriteIndexes[3], false, 10, 0, 0, 60);
+ _spriteIndexes[19] = startCycledSpriteSequence(_spriteIndexes[4], false, 5, 0, 1, 0);
+ _spriteIndexes[20] = startCycledSpriteSequence(_spriteIndexes[5], false, 10, 0, 2, 0);
+ _spriteIndexes[21] = startCycledSpriteSequence(_spriteIndexes[6], false, 6, 0, 0, 0);
+ _spriteIndexes[23] = startCycledSpriteSequence(_spriteIndexes[8], false, 6, 0, 10, 4);
+ _spriteIndexes[24] = startCycledSpriteSequence(_spriteIndexes[9], false, 6, 0, 32, 47);
activateHotspot(0x137, false); // SHIELD MODULATOR
// shield_panel_opened = 0;
@@ -206,16 +373,119 @@ void MadsSceneLogic::enterScene() {
if (_madsVm->globals()->previousScene != -1)
_madsVm->globals()->_globals[10] = 0;
if (_madsVm->globals()->previousScene != -2) {
- //playerPos = (100, 152);
+ _madsVm->_player._playerPos = Common::Point(100, 152);
}
- // TODO: EXTRA STUFF
+ if ((_madsVm->globals()->previousScene == 112) ||
+ ((_madsVm->globals()->previousScene != -2) && (_spriteIndexes[29] != 0))) {
+ // Returning from probe cutscene?
+ _spriteIndexes[29] = -1;
+ _madsVm->_player._playerPos = Common::Point(161, 123);
+ _madsVm->_player._direction = 9;
+
+ // TODO: Extra flags setting
+ _spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], false, 3, 0, 0, 0);
+ _madsVm->scene()->_sequenceList.setAnimRange(_spriteIndexes[25], 17, 17);
+ activateHotspot(0x47, false); // CHAIR
+ /*timer_unk1 = */_madsVm->scene()->_dynamicHotspots.add(0x47, 0x13F /*SIT_IN*/, -1,
+ Common::Rect(159, 84, 159+33, 84+36));
+
+ //if (_madsVm->globals()->previousScene == 112)
+ // room101Check();
+ } else {
+ _spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], false, 6, 0, 0, 0);
+ _madsVm->scene()->_sequenceList.setDepth(_spriteIndexes[26], 4);
+ }
+
+ _madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1);
+
+ if (_madsVm->globals()->_globals[10]) {
+ const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1);
+ _madsVm->scene()->loadAnimation(animName, 71);
+ _madsVm->_player._playerPos = Common::Point(68, 140);
+ _madsVm->_player._direction = 4;
+ _madsVm->_player._visible = false;
+ _madsVm->_player._stepEnabled = false;
+
+ dataMap()[0x56FC] = 0;
+ dataMap()[0x5482] = 0;
+ dataMap()[0x5484] = 30;
+ }
+
+ _madsVm->globals()->_dataMap[0x5486] = 0;
lowRoomsEntrySound();
}
+void MadsSceneLogic::doPreactions() {
+ warning("Still to do preactions logic");
+}
+
void MadsSceneLogic::doAction() {
+ warning("Still to do actions logic");
+}
+
+void MadsSceneLogic::doSceneStep() {
+ // TODO: Sound handling
+
+ switch (_madsVm->scene()->_abortTimers) {
+ case 70:
+ _madsVm->_sound->playSound(9);
+ break;
+ case 71:
+ _madsVm->globals()->_globals[10] = 0;
+ _madsVm->_player._visible = true;
+ _madsVm->_player._stepEnabled = true;
+
+ _madsVm->_player._priorTimer = _madsVm->_currentTimer - _madsVm->_player._ticksAmount;
+ break;
+ case 72:
+ case 73:
+ // TODO: Method that should be scripted
+ break;
+
+ default:
+ break;
+ }
+
+ // Wake up message sequence
+ Animation *anim = _madsVm->scene()->activeAnimation();
+ if (anim) {
+ if ((anim->getCurrentFrame() == 6) && (dataMap()[0x5482] == 0)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(49));
+ dataMap()[0x5484] += 14;
+ }
+ if ((anim->getCurrentFrame() == 7) && (dataMap()[0x5482] == 1)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(54));
+ dataMap()[0x5484] += 14;
+ }
+
+ if ((anim->getCurrentFrame() == 10) && (dataMap()[0x5482] == 2)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(55));
+ dataMap()[0x5484] += 14;
+ }
+
+ if ((anim->getCurrentFrame() == 17) && (dataMap()[0x5482] == 3)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(56));
+ dataMap()[0x5484] += 14;
+ }
+
+ if ((anim->getCurrentFrame() == 20) && (dataMap()[0x5482] == 4)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(50));
+ dataMap()[0x5484] += 14;
+ }
+ }
}
}
diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h
index a589556a21..ec6eff368b 100644
--- a/engines/m4/mads_logic.h
+++ b/engines/m4/mads_logic.h
@@ -29,17 +29,21 @@
#ifndef M4_MADS_LOGIC_H
#define M4_MADS_LOGIC_H
+#include "m4/mads_views.h"
+
namespace M4 {
class MadsSceneLogic {
private:
// Library interface methods
uint16 loadSpriteSet(uint16 suffixNum, uint16 sepChar);
- uint16 startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
- uint16 startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
- uint16 startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
+ uint16 startReversibleSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
+ uint16 startCycledSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
+ uint16 startSpriteSequence3(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
void activateHotspot(int idx, bool active);
void lowRoomsEntrySound();
+ void getPlayerSpritesPrefix();
+ void getPlayerSpritesPrefix2();
private:
int _sceneNumber;
int16 _spriteIndexes[50];
@@ -48,12 +52,21 @@ private:
const char *formAnimName(char sepChar, int16 suffixNum);
void getSceneSpriteSet();
void getAnimName();
+
+ IntStorage &dataMap();
public:
void selectScene(int sceneNum);
void setupScene();
void enterScene();
+ void doPreactions();
void doAction();
+ void doSceneStep();
+};
+
+class MadsGameLogic {
+public:
+ static void initialiseGlobals();
};
}
diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp
index 64f18fa11d..1ee6b3d265 100644
--- a/engines/m4/mads_menus.cpp
+++ b/engines/m4/mads_menus.cpp
@@ -49,7 +49,7 @@ RexMainMenuView::RexMainMenuView(MadsM4Engine *vm):
_skipFlag = false;
// Load the background for the Rex Nebular game
- _bgSurface = new M4Surface(width(), MADS_SURFACE_HEIGHT);
+ _bgSurface = new M4Surface();
_bgSurface->loadBackground(REX_MENUSCREEN, &_bgPalData);
_vm->_palette->addRange(_bgPalData);
_bgSurface->translate(_bgPalData);
@@ -163,7 +163,7 @@ bool RexMainMenuView::onEvent(M4EventType eventType, int32 param, int x, int y,
if (_highlightedIndex != -1) {
M4Sprite *spr = _menuItem->getFrame(_highlightedIndex);
const Common::Point &pt = _menuItemPosList[_highlightedIndex];
- spr->copyTo(this, pt.x, row + pt.y, 0);
+ spr->copyTo(this, pt.x, row + pt.y, spr->getTransparencyIndex());
}
}
} else {
@@ -211,10 +211,12 @@ void RexMainMenuView::updateState() {
M4Sprite *spr = _menuItem->getFrame(0);
itemSize = _menuItem->getFrame(0)->height();
spr->copyTo(this, _menuItemPosList[_menuItemIndex - 1].x,
- _menuItemPosList[_menuItemIndex - 1].y + row + (itemSize / 2) - (spr->height() / 2), 0);
+ _menuItemPosList[_menuItemIndex - 1].y + row + (itemSize / 2) - (spr->height() / 2),
+ spr->getTransparencyIndex());
delete _menuItem;
- copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0);
+ copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0,
+ spr->getTransparencyIndex());
}
// Get the next sprite set
@@ -275,7 +277,7 @@ void RexMainMenuView::updateState() {
_bgSurface->copyTo(this, 0, row);
M4Sprite *spr = _menuItem->getFrame(_frameIndex);
spr->copyTo(this, _menuItemPosList[_menuItemIndex - 1].x, _menuItemPosList[_menuItemIndex - 1].y +
- row + (itemSize / 2) - (spr->height() / 2), 0);
+ row + (itemSize / 2) - (spr->height() / 2), spr->getTransparencyIndex());
}
int RexMainMenuView::getHighlightedItem(int x, int y) {
@@ -293,7 +295,7 @@ int RexMainMenuView::getHighlightedItem(int x, int y) {
}
void RexMainMenuView::handleAction(MadsGameAction action) {
- MadsM4Engine *vm = _vm;
+ MadsEngine *vm = (MadsEngine *)_vm;
vm->_mouse->cursorOff();
vm->_viewManager->deleteView(this);
@@ -303,8 +305,7 @@ void RexMainMenuView::handleAction(MadsGameAction action) {
// Load a sample starting scene - note that, currently, calling loadScene automatically
// removes this menu screen from being displayed
vm->_mouse->cursorOn();
- vm->_scene->show();
- vm->_scene->loadScene(101);
+ vm->startScene(101);
return;
case SHOW_INTRO:
@@ -325,7 +326,7 @@ void RexMainMenuView::handleAction(MadsGameAction action) {
// Activate the scene display with the specified scene
bool altAdvert = vm->_random->getRandomNumber(1000) >= 500;
- vm->_scene->loadScene(altAdvert ? 995 : 996);
+ vm->startScene(altAdvert ? 995 : 996);
vm->_viewManager->addView(vm->_scene);
vm->_viewManager->refreshAll();
@@ -532,7 +533,7 @@ void DragonMainMenuView::updateState() {
_itemPalData.push_back(palData);
spr = _menuItem->getFrame(1);
- spr->copyTo(this, spr->xOffset - 140, spr->yOffset - spr->height(), (int)spr->getTransparentColor());
+ spr->copyTo(this, spr->xOffset - 140, spr->yOffset - spr->height(), spr->getTransparencyIndex());
_vm->_mouse->cursorOn();
}
@@ -808,7 +809,7 @@ void RexDialogView::setFrame(int frameNumber, int depth) {
}
void RexDialogView::initVars() {
- _word_8502C = -1;
+ _v8502C = -1;
_selectedLine = -1;
_lineIndex = 0;
_enterFlag = false;
diff --git a/engines/m4/mads_menus.h b/engines/m4/mads_menus.h
index e964c5866d..a0fc6fb3bc 100644
--- a/engines/m4/mads_menus.h
+++ b/engines/m4/mads_menus.h
@@ -136,7 +136,7 @@ protected:
int _dialogSelectedLine;
Common::StringArray _saveList;
- int _word_8502C;
+ int _v8502C;
int _selectedLine;
int _lineIndex;
bool _enterFlag;
diff --git a/engines/m4/mads_player.cpp b/engines/m4/mads_player.cpp
new file mode 100644
index 0000000000..de09e97640
--- /dev/null
+++ b/engines/m4/mads_player.cpp
@@ -0,0 +1,792 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "m4/m4.h"
+#include "m4/mads_player.h"
+#include "m4/mads_scene.h"
+
+namespace M4 {
+
+const int MadsPlayer::_directionListIndexes[32] = {
+ 0, 7, 4, 3, 6, 0, 2, 5, 0, 1, 9, 4, 1, 2, 7, 9, 3, 8, 9, 6, 7, 2, 3, 6, 1, 7, 9, 4, 7, 8, 0, 0
+};
+
+MadsPlayer::MadsPlayer() {
+ _playerPos = Common::Point(160, 78);
+ _ticksAmount = 3;
+ _forceRefresh = true;
+ _stepEnabled = true;
+ _visible = true;
+ _yScale = 0;
+ _moving = false;
+
+ _spriteListStart = 0;
+ //TODO:unknown vars
+ _special = 0;
+ _next = 0;
+ _unk4 = false;
+
+ _spritesChanged = true;
+
+ _direction = 0;
+ _newDirection = 0;
+ _priorTimer = 0;
+ _priorVisible = false;
+ _visible3 = false;
+ _spriteListIdx = 0;
+ _currentScale = 0;
+ strcpy(_spritesPrefix, "");
+ for (int idx = 0; idx < 8; ++idx)
+ _spriteSetsPresent[idx] = false;
+ _frameNum = 0;
+ _frameOffset = 0;
+ _unk1 = 0;
+ _frameCount = 0;
+ _frameListIndex = 0;
+ _actionIndex = 0;
+ _routeCount = 0;
+
+ resetActionList();
+}
+
+/**
+ * Loads the sprite set for the player
+ */
+bool MadsPlayer::loadSprites(const char *prefix) {
+ const char suffixList[8] = { '8', '9', '6', '3', '2', '7', '4', '1' };
+ char setName[80];
+ bool result = true;
+
+ if (prefix)
+ strcpy(_spritesPrefix, prefix);
+
+ _spriteSetCount = 0;
+ int prefixLen = strlen(_spritesPrefix);
+
+ if (prefixLen == 0) {
+ // No player sprites at at all
+ for (int idx = 0; idx < 8; ++idx)
+ _spriteSetsPresent[idx] = false;
+ } else {
+ strcpy(setName, "*");
+ strcat(setName, _spritesPrefix);
+ strcat(setName, "_0.SS");
+
+ char *digitP = strchr(setName, '_') + 1;
+
+ for (int idx = 0; idx < 8; ++idx) {
+ *digitP = suffixList[idx];
+ _spriteSetsPresent[idx] = true;
+
+ int setIndex = _madsVm->scene()->_spriteSlots.addSprites(setName, true, SPRITE_SET_CHAR_INFO);
+ if (setIndex < 0) {
+ if (idx < 5)
+ break;
+ _spriteSetsPresent[idx] = false;
+ } else {
+ ++_spriteSetCount;
+ }
+
+ if (idx == 0)
+ _spriteListStart = setIndex;
+ }
+
+ result = 0;
+ // TODO: Unknown flag
+ _spritesChanged = false;
+ }
+
+ return result;
+}
+
+/**
+ * Called each frame to update the display of the player
+ */
+void MadsPlayer::update() {
+ if (_forceRefresh || (_visible != _priorVisible)) {
+ // If there's an existing player sprite visible, flag it for expiry
+ int slotIndex = getSpriteSlot();
+ if (slotIndex >= 0)
+ _madsVm->scene()->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE;
+
+ // Figure out the depth for the sprite
+ int newDepth = 1;
+ int yp = MIN(_playerPos.y, (int16)155);
+
+ for (int idx = 1; idx < 15; ++idx) {
+ if (_madsVm->scene()->getSceneResources()._depthBands[newDepth] >= yp)
+ newDepth = idx + 1;
+ }
+ _currentDepth = newDepth;
+
+ // Get the scale
+ int newScale = getScale(_playerPos.y);
+ _currentScale = MIN(newScale, 100);
+
+ if (_visible) {
+ // Player sprite needs to be rendered
+ MadsSpriteSlot slot;
+ slot.spriteType = FOREGROUND_SPRITE;
+ slot.seqIndex = PLAYER_SEQ_INDEX;
+ slot.spriteListIndex = _spriteListStart + _spriteListIdx;
+ slot.frameNumber = _frameOffset + _frameNum;
+ slot.xp = _playerPos.x;
+ slot.yp = _playerPos.y + (_yScale * newScale) / 100;
+ slot.depth = newDepth;
+ slot.scale = newScale;
+
+ if (slotIndex >= 0) {
+ // Check if the existing player slot has the same details, and can be re-used
+ MadsSpriteSlot &s2 = _madsVm->scene()->_spriteSlots[slotIndex];
+ bool equal = (s2.seqIndex == slot.seqIndex) && (s2.spriteListIndex == slot.spriteListIndex)
+ && (s2.frameNumber == slot.frameNumber) && (s2.xp == slot.xp) && (s2.yp == slot.yp)
+ && (s2.depth == slot.depth) && (s2.scale == slot.scale);
+
+ if (equal)
+ // Undo the prior expiry of the player sprite
+ s2.spriteType = SPRITE_ZERO;
+ else
+ slotIndex = -1;
+ }
+
+ if (slotIndex < 0) {
+ // New slot needed, so allocate one and copy the slot data
+ slotIndex = _madsVm->scene()->_spriteSlots.getIndex();
+ _madsVm->scene()->_spriteSlots[slotIndex] = slot;
+ }
+
+ // TODO: Meaning of _v844c0 block
+
+ }
+ }
+
+ _visible3 = _priorVisible = _visible;
+ _forceRefresh = false;
+}
+
+/**
+ * Updates the animation frame for the player
+ */
+void MadsPlayer::updateFrame() {
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+
+ if (!spriteSet._charInfo->_numEntries) {
+ _frameNum = 1;
+ } else {
+ _frameListIndex = _actionList[_actionIndex];
+
+ if (!_visible) {
+ _unk2 = 0;
+ } else {
+ _unk2 = _actionList2[_actionIndex];
+
+ if (_actionIndex > 0)
+ --_actionIndex;
+ }
+
+ // Set the player frame number
+ int frameIndex = ABS(_frameListIndex);
+ _frameNum = (_frameListIndex <= 0) ? spriteSet._charInfo->_frameList[frameIndex] :
+ spriteSet._charInfo->_frameList2[frameIndex];
+
+ // Set next waiting period in ticks
+ if (frameIndex == 0)
+ setTicksAmount();
+ else
+ _madsVm->_player._ticksAmount = spriteSet._charInfo->_ticksList[frameIndex];
+ }
+}
+
+void MadsPlayer::setupFrame() {
+ resetActionList();
+ _frameOffset = 0;
+ _spriteListIdx = _directionListIndexes[_direction];
+ if (!_spriteSetsPresent[_spriteListIdx]) {
+ // Direction isn't present, so use alternate direction, with entries flipped
+ _spriteListIdx -= 4;
+ _frameOffset = 0x8000;
+ }
+
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+ _unk1 = MAX(spriteSet._charInfo->_unk1, 100);
+ setTicksAmount();
+
+ _frameCount = spriteSet._charInfo->_totalFrames;
+ if (_frameCount == 0)
+ _frameCount = spriteSet.getCount();
+
+ _yScale = spriteSet._charInfo->_yScale;
+
+ if ((_frameNum <= 0) || (_frameNum > _frameCount))
+ _frameNum = 1;
+ _forceRefresh = true;
+}
+
+void MadsPlayer::step() {
+ if (_visible && _stepEnabled && !_moving && (_direction == _newDirection) && (_madsVm->_currentTimer >= GET_GLOBAL32(2))) {
+ if (_actionIndex == 0) {
+ int randVal = _vm->_random->getRandomNumber(29999);
+
+ if (GET_GLOBAL(0) == SEX_MALE) {
+ switch (_direction) {
+ case 1:
+ case 3:
+ case 7:
+ case 9:
+ if (randVal < 200) {
+ queueAction(-1, 0);
+ queueAction(1, 0);
+ }
+ break;
+
+ case 2:
+ if (randVal < 500) {
+ for (int i = 0; i < 10; ++i)
+ queueAction((randVal < 250) ? 1 : 2, 0);
+ } else if (randVal < 750) {
+ for (int i = 0; i < 5; ++i)
+ queueAction(1, 0);
+ queueAction(0, 0);
+ for (int i = 0; i < 5; ++i)
+ queueAction(2, 0);
+ }
+ break;
+
+ case 4:
+ case 6:
+ if (randVal < 500) {
+ for (int i = 0; i < 10; ++i)
+ queueAction(1, 0);
+ }
+ break;
+
+ case 5:
+ case 8:
+ if (randVal < 200) {
+ queueAction(-1, 0);
+ queueAction(1, 0);
+ }
+ break;
+ }
+ }
+ }
+
+ SET_GLOBAL32(2, GET_GLOBAL32(2) + 6);
+ }
+
+ if (GET_GLOBAL(138) == 1) {
+ uint32 diff = _madsVm->_currentTimer - GET_GLOBAL32(142);
+ if (diff > 60) {
+ SET_GLOBAL32(144, GET_GLOBAL32(144) + 1);
+ } else {
+ SET_GLOBAL32(144, GET_GLOBAL32(144) + diff);
+ }
+
+ SET_GLOBAL32(142, _madsVm->_currentTimer);
+ }
+}
+
+void MadsPlayer::nextFrame() {
+ if (_madsVm->_currentTimer >= (_priorTimer + _ticksAmount)) {
+ _priorTimer = _madsVm->_currentTimer;
+
+ if (_moving)
+ move();
+ else
+ idle();
+
+ // Post update logic
+ if (_moving) {
+ ++_frameNum;
+ if (_frameNum > _frameCount)
+ _frameNum = 1;
+ _forceRefresh = true;
+ } else if (!_forceRefresh) {
+ idle();
+ }
+
+ // Final update
+ update();
+ }
+}
+
+void MadsPlayer::setDest(int destX, int destY, int facing) {
+ resetActionList();
+ setTicksAmount();
+ _moving = true;
+ _destFacing = facing;
+
+ _madsVm->scene()->getSceneResources().setRouteNode(_madsVm->scene()->getSceneResources()._nodes.size() - 2,
+ _playerPos, _madsVm->scene()->_depthSurface);
+ _madsVm->scene()->getSceneResources().setRouteNode(_madsVm->scene()->getSceneResources()._nodes.size() - 1,
+ Common::Point(destX, destY), _madsVm->scene()->_depthSurface);
+
+ bool v = _madsVm->scene()->getDepthHighBit(Common::Point(destX, destY));
+ setupRoute(v);
+ _next = 0;
+
+ if (_routeCount > 0) {
+ Common::Point srcPos = _playerPos;
+ for (int routeCtr = _routeCount - 1; (routeCtr >= 0) && (_next == 0); --routeCtr) {
+ int idx = _routeIndexes[routeCtr];
+ const Common::Point &pt = _madsVm->scene()->getSceneResources()._nodes[idx].pt;
+
+ _next = scanPath(_madsVm->scene()->_depthSurface, srcPos, pt);
+ srcPos = pt;
+ }
+ }
+}
+
+
+int MadsPlayer::getScale(int yp) {
+ MadsSceneResources &r = _madsVm->scene()->getSceneResources();
+
+ int scale = (r.bandsRange() == 0) ? r._maxScale : (yp - r._yBandsStart) * r.scaleRange() / r.bandsRange()
+ + r._minScale;
+
+ return MIN(scale, 100);
+}
+
+/**
+ * Scans through the scene's sprite slot list to find any sprite displaying the player
+ */
+int MadsPlayer::getSpriteSlot() {
+ MadsSpriteSlots &slots = _madsVm->scene()->_spriteSlots;
+ for (int i = 0; i < slots.startIndex; ++i) {
+ if ((slots[i].seqIndex == PLAYER_SEQ_INDEX) && (slots[i].spriteType >= SPRITE_ZERO))
+ return i;
+ }
+ return -1;
+}
+
+void MadsPlayer::setTicksAmount() {
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+ _madsVm->_player._ticksAmount = spriteSet._charInfo->_ticksAmount;
+ if (_madsVm->_player._ticksAmount == 0)
+ _madsVm->_player._ticksAmount = 6;
+}
+
+void MadsPlayer::resetActionList() {
+ _actionList[0] = 0;
+ _actionList2[0] = 0;
+ _actionIndex = 0;
+ _unk2 = 0;
+ _unk3 = 0;
+}
+
+int MadsPlayer::queueAction(int action1, int action2) {
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+
+ if ((action1 < spriteSet._charInfo->_numEntries) && (_actionIndex < 11)) {
+ ++_actionIndex;
+ _actionList[_actionIndex] = action1;
+ _actionList2[_actionIndex] = action2;
+ return false;
+ }
+
+ return true;
+}
+
+void MadsPlayer::idle() {
+ if (_direction != _newDirection) {
+ // The direction has changed, so reset for new direction
+ dirChanged();
+ return;
+ }
+
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+ if (spriteSet._charInfo->_numEntries == 0)
+ // No entries, so exit immediately
+ return;
+
+ int frameIndex = ABS(_frameListIndex);
+ int direction = (_frameListIndex < 0) ? -1 : 1;
+
+ if (frameIndex >= spriteSet._charInfo->_numEntries)
+ // Reset back to the start of the list
+ _frameListIndex = 0;
+ else {
+ _frameNum += direction;
+ _forceRefresh = true;
+
+ if (spriteSet._charInfo->_frameList2[frameIndex] < _frameNum) {
+ _unk3 = _unk2;
+ updateFrame();
+ }
+ if (spriteSet._charInfo->_frameList[frameIndex] < _frameNum) {
+ _unk3 = _unk2;
+ updateFrame();
+ }
+ }
+}
+
+void MadsPlayer::move() {
+ bool routeFlag = false;
+
+ if (_moving) {
+ int idx = _routeCount;
+ while (!_v844C0 && (_destPos.x == _playerPos.x) && (_destPos.y == _playerPos.y)) {
+ if (idx != 0) {
+ --idx;
+ SceneNode &node = _madsVm->scene()->getSceneResources()._nodes[_routeIndexes[idx]];
+ _destPos = node.pt;
+ routeFlag = true;
+ } else if (_v844BE == idx) {
+ // End of walking path
+ _routeCount = 0;
+ _moving = false;
+ turnToDestFacing();
+ routeFlag = true;
+ idx = _routeCount;
+ } else {
+ _v844C0 = _v844BE;
+ _v844BC = true;
+ _v844BE = 0;
+ _stepEnabled = true;
+ routeFlag = false;
+ }
+
+ if (!_moving)
+ break;
+ }
+ _routeCount = idx;
+ }
+
+ if (routeFlag && _moving)
+ startMovement();
+
+ if (_newDirection != _direction)
+ dirChanged();
+ else if (!_moving)
+ updateFrame();
+
+ int var1 = _unk1;
+ if (_unk4 && (_hypotenuse > 0)) {
+ int v1 = -(_currentScale - 100) * (_posDiff.x - 1) / _hypotenuse + _currentScale;
+ var1 = MAX(1, 10000 / (v1 * _currentScale * var1));
+ }
+
+ if (!_moving || (_direction != _newDirection))
+ return;
+
+ Common::Point newPos = _playerPos;
+
+ if (_v8452E < var1) {
+ do {
+ if (_v8452C < _posDiff.x)
+ _v8452C += _posDiff.y;
+ if (_v8452C >= _posDiff.x) {
+ if ((_posChange.y > 0) || (_v844C0 != 0))
+ newPos.y += _yDirection;
+ --_posChange.y;
+ _v8452C -= _posDiff.x;
+ }
+
+ if (_v8452C < _posDiff.x) {
+ if ((_posChange.x > 0) || (_v844C0 != 0))
+ newPos.x += _xDirection;
+ --_posChange.x;
+ }
+
+ if ((_v844BC == 0) && (_v844C0 == 0) && (_v844BE == 0)) {
+ routeFlag = _madsVm->scene()->getDepthHighBit(newPos);
+
+ if (_special == 0)
+ _special = _madsVm->scene()->getDepthHighBits(newPos);
+ }
+
+ _v8452E += _v84530;
+
+ } while ((_v8452E < var1) && !routeFlag && ((_posChange.x > 0) || (_posChange.y > 0) || (_v844C0 != 0)));
+ }
+
+ _v8452E -= var1;
+
+ if (routeFlag)
+ moveComplete();
+ else {
+ if (!_v844C0) {
+ // If the move is complete, make sure the position is exactly on the given destination
+ if (_posChange.x == 0)
+ newPos.x = _destPos.x;
+ if (_posChange.y == 0)
+ newPos.y = _destPos.y;
+ }
+
+ _playerPos = newPos;
+ }
+}
+
+void MadsPlayer::dirChanged() {
+ int dirIndex = 0, dirIndex2 = 0;
+ int newDir = 0, newDir2 = 0;
+
+ if (_direction != _newDirection) {
+ // Find the index for the given direction in the player direction list
+ int tempDir = _direction;
+ do {
+ ++dirIndex;
+ newDir += tempDir;
+ tempDir = _directionListIndexes[tempDir + 10];
+ } while (tempDir != _newDirection);
+ }
+
+
+ if (_direction != _newDirection) {
+ // Find the index for the given direction in the player direction list
+ int tempDir = _direction;
+ do {
+ ++dirIndex2;
+ newDir2 += tempDir;
+ tempDir = _directionListIndexes[tempDir + 20];
+ } while (tempDir != _newDirection);
+ }
+
+ int diff = dirIndex - dirIndex2;
+ if (diff == 0)
+ diff = newDir - newDir2;
+
+ _direction = (diff >= 0) ? _directionListIndexes[_direction + 20] : _directionListIndexes[_direction + 10];
+ setupFrame();
+ if ((_direction == _newDirection) && !_moving)
+ updateFrame();
+
+ _priorTimer += 1;
+}
+
+void MadsPlayer::moveComplete() {
+ reset();
+ //todo: Unknown flag
+}
+
+void MadsPlayer::reset() {
+ _destPos = _playerPos;
+ _destFacing = 5;
+ _newDirection = _direction;
+
+ _madsVm->scene()->_action._startWalkFlag = false;
+ _madsVm->scene()->_action._walkFlag = false;
+ _moving = false;
+ _v844BC = false;
+ _v844C0 = false;
+ _v844BE = 0;
+ _next = 0;
+ _routeCount = 0;
+}
+
+/**
+ * Scans along an edge connecting two points within the depths/walk surface, and returns the information of the first
+ * pixel high nibble encountered with a non-zero value
+ */
+int MadsPlayer::scanPath(M4Surface *depthSurface, const Common::Point &srcPos, const Common::Point &destPos) {
+ // For compressed depth surfaces, always return 0
+ if (_madsVm->scene()->getSceneResources()._depthStyle != 2)
+ return 0;
+
+ int yDiff = destPos.y - srcPos.y;
+ int yAmount = MADS_SURFACE_WIDTH;
+
+ if (yDiff < 0) {
+ yDiff = -yDiff;
+ yAmount = -yAmount;
+ }
+
+ int xDiff = destPos.x - srcPos.y;
+ int xDirection = 1;
+ int xAmount = 0;
+ if (xDiff < 0) {
+ xDiff = -xDiff;
+ xDirection = -xDirection;
+ xAmount = MIN(yDiff, xDiff);
+ }
+
+ ++xDiff;
+ ++yDiff;
+
+ const byte *srcP = depthSurface->getBasePtr(srcPos.x, srcPos.y);
+ int index = xAmount;
+
+ // Outer horizontal movement loop
+ for (int yIndex = 0; yIndex < yDiff; ++yIndex) {
+ index += yDiff;
+ int v = (*srcP & 0x7F) >> 4;
+ if (v)
+ return v;
+
+ // Inner loop for handling vertical movement
+ while (index >= xDiff) {
+ index -= xDiff;
+
+ v = (*srcP & 0x7F) >> 4;
+ if (v)
+ return v;
+
+ srcP += yAmount;
+ }
+
+ srcP += xDirection;
+ }
+
+ return 0;
+}
+
+/**
+ * Starts a player moving to a given destination
+ */
+void MadsPlayer::startMovement() {
+ int xDiff = _destPos.x - _playerPos.x;
+ int yDiff = _destPos.y - _playerPos.y;
+ int srcScale = getScale(_playerPos.y);
+ int destScale = getScale(_destPos.y);
+
+ // Sets the X direction
+ if (xDiff > 0)
+ _xDirection = 1;
+ else if (xDiff < 0)
+ _xDirection = -1;
+ else
+ _xDirection = 0;
+
+ // Sets the Y direction
+ if (yDiff > 0)
+ _yDirection = 1;
+ else if (yDiff < 0)
+ _yDirection = -1;
+ else
+ _yDirection = 0;
+
+ xDiff = ABS(xDiff);
+ yDiff = ABS(yDiff);
+ int scaleDiff = ABS(srcScale - destScale);
+
+ int xAmt100 = xDiff * 100;
+ int yAmt100 = yDiff * 100;
+ int xAmt33 = xDiff * 33;
+
+ int scaleAmount = (_unk4 ? scaleDiff * 3 : 0) + 100 * yDiff / 100;
+ int scaleAmount100 = scaleAmount * 100;
+
+ // Figure out direction that will need to be moved in
+ int majorDir;
+ if (xDiff == 0)
+ majorDir = 1;
+ else if (yDiff == 0)
+ majorDir = 3;
+ else {
+ if ((scaleAmount < xDiff) && ((xAmt33 / scaleAmount) >= 141))
+ majorDir = 3;
+ else if (yDiff <= xDiff)
+ majorDir = 2;
+ else if ((scaleAmount100 / xDiff) >= 141)
+ majorDir = 1;
+ else
+ majorDir = 2;
+ }
+
+ switch (majorDir) {
+ case 1:
+ _newDirection = (_yDirection <= 0) ? 8 : 2;
+ break;
+ case 2: {
+ _newDirection = ((_yDirection <= 0) ? 9 : 3) - ((_xDirection <= 0) ? 2 : 0);
+ break;
+ }
+ case 3:
+ _newDirection = (_xDirection <= 0) ? 4 : 6;
+ break;
+ default:
+ break;
+ }
+
+ _hypotenuse = SqrtF16(xAmt100 * xAmt100 + yAmt100 * yAmt100);
+ _posDiff.x = xDiff + 1;
+ _posDiff.y = yDiff + 1;
+ _posChange.x = xDiff;
+ _posChange.y = yDiff;
+
+ int majorChange = MAX(xDiff, yDiff);
+ _v84530 = (majorChange == 0) ? 0 : _hypotenuse / majorChange;
+
+ if (_playerPos.x > _destPos.x)
+ _v8452C = MAX(_posChange.x, _posChange.y);
+ else
+ _v8452C = 0;
+
+ _hypotenuse /= 100;
+ _v8452E = -_v84530;
+}
+
+void MadsPlayer::turnToDestFacing() {
+ if (_destFacing != 5)
+ _newDirection = _destFacing;
+}
+
+void MadsPlayer::setupRoute(bool bitFlag) {
+ // Reset the flag set of nodes in use
+ SceneNodeList &nodes = _madsVm->scene()->getSceneResources()._nodes;
+ for (uint i = 0; i < nodes.size(); ++i)
+ nodes[i].active = false;
+
+ // Start constructing route node list
+ _routeLength = 0x3FFF;
+ _routeCount = 0;
+
+ setupRouteNode(_tempRoute, nodes.size() - 1, bitFlag ? 0xC000 : 0x8000, 0);
+}
+
+void MadsPlayer::setupRouteNode(int *routeIndexP, int nodeIndex, int flags, int routeLength) {
+ SceneNodeList &nodes = _madsVm->scene()->getSceneResources()._nodes;
+ SceneNode &currentNode = nodes[nodeIndex];
+ currentNode.active = true;
+
+ *routeIndexP++ = nodeIndex;
+
+ int subIndex = nodes.size() - 2;
+ int indexVal = nodes[nodeIndex].indexes[subIndex];
+ if (indexVal & flags) {
+ routeLength += indexVal & 0x3FFF;
+ if (routeLength < _routeLength) {
+ // Found a new shorter route to destination, so set up the route with the found one
+ Common::copy(_tempRoute, routeIndexP, _routeIndexes);
+ _routeCount = routeIndexP - _tempRoute;
+ _routeLength = indexVal & 0x3FFF;
+ }
+ } else {
+ for (int idx = nodes.size() - 2; idx > 0; --idx) {
+ int nodePos = idx - 1;
+ if (!nodes[nodePos].active && ((currentNode.indexes[nodePos] & flags) != 0))
+ setupRouteNode(routeIndexP, nodePos, 0x8000, indexVal & 0x3fff);
+ }
+ }
+
+ currentNode.active = false;
+}
+
+} // End of namespace M4
diff --git a/engines/m4/mads_player.h b/engines/m4/mads_player.h
new file mode 100644
index 0000000000..6a9b7b4ca1
--- /dev/null
+++ b/engines/m4/mads_player.h
@@ -0,0 +1,116 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 M4_MADS_PLAYER_H
+#define M4_MADS_PLAYER_H
+
+#include "common/scummsys.h"
+#include "m4/mads_scene.h"
+
+namespace M4 {
+
+#define PLAYER_SEQ_INDEX -2
+
+class MadsPlayer {
+private:
+ int getScale(int yp);
+ int getSpriteSlot();
+ void setTicksAmount();
+ void resetActionList();
+ int queueAction(int v0, int v1);
+ void idle();
+ void move();
+ void dirChanged();
+ void reset();
+ int scanPath(M4Surface *depthSurface, const Common::Point &srcPos, const Common::Point &destPos);
+ void startMovement();
+ void setupRouteNode(int *routeIndexP, int nodeIndex, int flags, int routeLength);
+public:
+ char _spritesPrefix[16];
+ int _spriteSetCount;
+ bool _spriteSetsPresent[8];
+ Common::Point _playerPos;
+ Common::Point _destPos;
+ Common::Point _posChange;
+ Common::Point _posDiff;
+ int _hypotenuse;
+ uint32 _priorTimer;
+ uint _ticksAmount;
+ int16 _direction, _newDirection;
+ bool _stepEnabled;
+ bool _visible, _priorVisible;
+ bool _visible3;
+ bool _forceRefresh;
+ int16 _currentScale;
+ int16 _yScale;
+ int16 _currentDepth;
+ int16 _spriteListStart, _spriteListIdx;
+ bool _spritesChanged;
+ uint16 _frameOffset, _frameNum;
+ bool _moving;
+ int _unk1;
+ int _frameCount;
+ int _frameListIndex;
+ int _actionIndex;
+ int _actionList[12];
+ int _actionList2[12];
+ int _unk2;
+ int _unk3;
+ int _xDirection, _yDirection;
+ int _destFacing;
+ int _special;
+ int _next;
+ int _routeCount;
+ int _routeOffset;
+ int _tempRoute[MAX_ROUTE_NODES];
+ int _routeIndexes[MAX_ROUTE_NODES];
+ bool _unk4;
+ bool _v844BC;
+ int _v844BE;
+ bool _v844C0;
+ int _v8452E;
+ int _v8452C;
+ int _v84530;
+ int _routeLength;
+
+ static const int _directionListIndexes[32];
+public:
+ MadsPlayer();
+
+ bool loadSprites(const char *prefix);
+ void update();
+ void updateFrame();
+ void setupFrame();
+ void step();
+ void nextFrame();
+ void setDest(int destX, int destY, int facing);
+ void turnToDestFacing();
+ void setupRoute(bool bitFlag);
+ void moveComplete();
+};
+
+} // End of namespace M4
+
+#endif
diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp
index 8073db32db..d44fa2a753 100644
--- a/engines/m4/mads_scene.cpp
+++ b/engines/m4/mads_scene.cpp
@@ -37,18 +37,42 @@
#include "m4/mads_views.h"
#include "m4/compression.h"
#include "m4/staticres.h"
+#include "m4/animation.h"
namespace M4 {
+static const int INV_ANIM_FRAME_SPEED = 2;
+static const int INVENTORY_X = 160;
+static const int INVENTORY_Y = 159;
+static const int SCROLLER_DELAY = 200;
+
+//--------------------------------------------------------------------------
+
+void SceneNode::load(Common::SeekableReadStream *stream) {
+ // Get the next data block
+ pt.x = stream->readUint16LE();
+ pt.y = stream->readUint16LE();
+
+ for (int i = 0; i < MAX_ROUTE_NODES; ++i)
+ indexes[i] = stream->readUint16LE();
+}
+
+//--------------------------------------------------------------------------
+
MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) {
_vm = vm;
+ _activeAnimation = NULL;
+ MadsView::_bgSurface = Scene::_backgroundSurface;
+ MadsView::_depthSurface = Scene::_walkSurface;
_interfaceSurface = new MadsInterfaceView(vm);
- for (int i = 0; i < 3; ++i)
- actionNouns[i] = 0;
+ _showMousePos = false;
+ _mouseMsgIndex = -1;
}
MadsScene::~MadsScene() {
+ delete _activeAnimation;
+ _activeAnimation = NULL;
leaveScene();
_vm->_viewManager->deleteView(_interfaceSurface);
}
@@ -56,8 +80,10 @@ MadsScene::~MadsScene() {
/**
* Secondary scene loading code
*/
-void MadsScene::loadScene2(const char *aaName) {
+void MadsScene::loadScene2(const char *aaName, int sceneNumber) {
// TODO: Completely finish
+ _madsVm->globals()->previousScene = _madsVm->globals()->sceneNumber;
+ _madsVm->globals()->sceneNumber = sceneNumber;
_spriteSlots.clear();
_sequenceList.clear();
@@ -68,6 +94,13 @@ void MadsScene::loadScene2(const char *aaName) {
// Load scene walk paths
loadSceneCodes(_currentScene);
+
+ // Initialise the scene animation
+ uint16 flags = 0x4100;
+ if (_madsVm->globals()->_config.textWindowStill)
+ flags |= 0x200;
+
+ _sceneAnimation->initialise(aaName, flags, _interfaceSurface, NULL);
}
/**
@@ -76,27 +109,12 @@ void MadsScene::loadScene2(const char *aaName) {
void MadsScene::loadSceneTemporary() {
/* Existing code that eventually needs to be replaced with the proper MADS code */
// Set system palette entries
- _vm->_palette->blockRange(0, 7);
+ _vm->_palette->blockRange(0, 18);
RGB8 sysColors[3] = { {0x1f<<2, 0x2d<<2, 0x31<<2, 0}, {0x24<<2, 0x37<<2, 0x3a<<2, 0},
{0x00<<2, 0x10<<2, 0x16<<2, 0}};
_vm->_palette->setPalette(&sysColors[0], 4, 3);
- _backgroundSurface->loadBackground(_currentScene, &_palData);
- _vm->_palette->addRange(_palData);
- _backgroundSurface->translate(_palData);
-
- if (_currentScene < 900) {
- _interfaceSurface->madsloadInterface(0, &_interfacePal);
- _vm->_palette->addRange(_interfacePal);
- _interfaceSurface->translate(_interfacePal);
- _backgroundSurface->copyFrom(_interfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44);
-
- _interfaceSurface->initialise();
- }
-
- // Don't load other screen resources for system screens
- if (_currentScene >= 900)
- return;
+ _interfaceSurface->initialise();
loadSceneHotspots(_currentScene);
@@ -112,25 +130,57 @@ void MadsScene::loadScene(int sceneNumber) {
// Handle common scene setting
Scene::loadScene(sceneNumber);
-
- _madsVm->globals()->previousScene = _madsVm->globals()->sceneNumber;
- _madsVm->globals()->sceneNumber = sceneNumber;
+ _madsVm->globals()->_nextSceneId = sceneNumber;
// Existing ScummVM code that needs to be eventually replaced with MADS code
loadSceneTemporary();
+ _madsVm->_player._spritesChanged = true;
+ _madsVm->globals()->clearQuotes();
+ _dynamicHotspots.reset();
+
// Signal the script engine what scene is to be active
_sceneLogic.selectScene(sceneNumber);
- _sceneLogic.setupScene();
// Add the scene if necessary to the list of scenes that have been visited
_vm->globals()->addVisitedScene(sceneNumber);
+ if (_vm->getGameType() == GType_RexNebular)
+ _sceneLogic.setupScene();
+
+ // TODO: Unknown code
+
// Secondary scene load routine
- loadScene2("*I0.AA");
+ if (_vm->getGameType() == GType_RexNebular)
+ // Secondary scene load routine
+ loadScene2("*I0.AA", sceneNumber);
+
+ _madsVm->_player.loadSprites(NULL);
+
+ switch (_madsVm->globals()->_config.screenFades) {
+ case 0:
+ _abortTimers2 = 2;
+ break;
+ case 2:
+ _abortTimers2 = 21;
+ break;
+ default:
+ _abortTimers2 = 20;
+ break;
+ }
+ _abortTimers = 0;
+ _abortTimersMode2 = ABORTMODE_1;
+
// Do any scene specific setup
- _sceneLogic.enterScene();
+ if (_vm->getGameType() == GType_RexNebular)
+ _sceneLogic.enterScene();
+
+ // Miscellaneous player setup
+ _madsVm->_player._destPos = _madsVm->_player._destPos;
+ _madsVm->_player._newDirection = _madsVm->_player._direction;
+ _madsVm->_player.setupFrame();
+ _madsVm->_player.updateFrame();
// Purge resources
_vm->res()->purge();
@@ -145,26 +195,28 @@ void MadsScene::loadSceneHotspots(int sceneNumber) {
int hotspotCount = hotspotStream->readUint16LE();
delete hotspotStream;
- _sceneResources.hotspotCount = hotspotCount;
-
hotspotStream = hotSpotData.getItemStream(1);
// Clear current hotspot lists
_sceneResources.hotspots->clear();
-
- _sceneResources.hotspots->loadHotSpots(hotspotStream, _sceneResources.hotspotCount);
+ _sceneResources.hotspots->loadHotSpots(hotspotStream, hotspotCount);
delete hotspotStream;
}
void MadsScene::leaveScene() {
_sceneResources.hotspots->clear();
- _sceneResources.props->clear();
+ _sceneResources.dynamicHotspots->clear();
delete _sceneResources.hotspots;
- delete _sceneResources.props;
+ delete _sceneResources.dynamicHotspots;
delete _walkSurface;
+ if (_activeAnimation) {
+ delete _activeAnimation;
+ _activeAnimation = NULL;
+ }
+
Scene::leaveScene();
}
@@ -183,69 +235,46 @@ void MadsScene::loadSceneCodes(int sceneNumber, int index) {
sceneS = walkData.getItemStream(0);
_walkSurface->loadCodesMads(sceneS);
_vm->res()->toss(filename);
- } else if (_vm->getGameType() == GType_RexNebular) {
- // For Rex Nebular, the walk areas are part of the scene info
- byte *destP = _walkSurface->getBasePtr(0, 0);
- const byte *srcP = _sceneResources.walkData;
- byte runLength;
- while ((runLength = *srcP++) != 0) {
- Common::set_to(destP, destP + runLength, *srcP++);
- destP += runLength;
- }
}
}
-void MadsScene::checkHotspotAtMousePos(int x, int y) {
+void MadsScene::mouseMove(int x, int y) {
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
if (currentHotSpot != NULL) {
_vm->_mouse->setCursorNum(currentHotSpot->getCursor());
- // This is the "easy" interface, which updates the status text when the mouse is moved
- // TODO: toggle this code for easy/normal interface mode
- char statusText[50];
- int verbId = 0;//***DEBUG****_currentAction;
- if (verbId == kVerbNone)
- verbId = currentHotSpot->getVerbID();
- if (verbId == kVerbNone)
- verbId = kVerbWalkTo;
+ _action._selectedRow = -1;
+ _action._actionMode = ACTMODE_NONE;
+ _action._actionMode2 = ACTMODE2_4;
+ _action._hotspotId = currentHotSpot->getIndex();
- sprintf(statusText, "%s %s\n", _madsVm->globals()->getVocab(verbId), currentHotSpot->getVocab());
-
- statusText[0] = toupper(statusText[0]); // capitalize first letter
- setStatusText(statusText);
} else {
_vm->_mouse->setCursorNum(0);
- setStatusText("");
}
}
void MadsScene::leftClick(int x, int y) {
- HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
- if (currentHotSpot != NULL) {
- char statusText[50];
- if (currentHotSpot->getVerbID() != 0) {
- sprintf(statusText, "%s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
- } else {
- sprintf(statusText, "%s %s\n", _madsVm->globals()->getVocab(kVerbWalkTo), currentHotSpot->getVocab());
- }
-
- statusText[0] = toupper(statusText[0]); // capitalize first letter
- setStatusText(statusText);
- }
+ // TODO: figure out the rest of Scene_leftClick, and implements relevant parts in the interface class
+ _action._v86F4C = -1;
+ _action._v86F4E = 0;
+ _customDest = _madsVm->_mouse->currentPos();
+ _action._selectedAction = -1;
+ _action._v86F4A = true;
}
void MadsScene::rightClick(int x, int y) {
- // ***DEBUG*** - sample dialog display
- int idx = 3; //_madsVm->_globals->messageIndexOf(0x277a);
- const char *msg = _madsVm->globals()->loadMessage(idx);
- Dialog *dlg = new Dialog(_vm, msg, "TEST DIALOG");
- _vm->_viewManager->addView(dlg);
- _vm->_viewManager->moveToFront(dlg);
+ if (_vm->getGameType() == GType_RexNebular) {
+ // ***DEBUG*** - sample dialog display
+ int idx = 3; //_madsVm->_globals->messageIndexOf(0x277a);
+ const char *msg = _madsVm->globals()->loadMessage(idx);
+ Dialog *dlg = new Dialog(_vm, msg, "TEST DIALOG");
+ _vm->_viewManager->addView(dlg);
+ _vm->_viewManager->moveToFront(dlg);
+ }
}
void MadsScene::setAction(int action, int objectId) {
VALIDATE_MADS;
- char statusText[50];
error("todo");
// TODO: Actually executing actions directly for objects. Also, some object actions are special in that
@@ -262,7 +291,7 @@ void MadsScene::setAction(int action, int objectId) {
_currentAction = action;
}
*/
- setStatusText(statusText);
+// setStatusText(statusText);
}
/**
@@ -277,75 +306,166 @@ void MadsScene::drawElements() {
void MadsScene::update() {
- // Copy the bare scene in
- _backgroundSurface->copyTo(this);
-
// Draw all the various elements
drawElements();
_action.set();
- const char *sStatusText = _action.statusText();
+}
- // Handle display of any status text
- if (sStatusText[0]) {
- // Text colors are inverted in Dragonsphere
- if (_vm->getGameType() == GType_DragonSphere)
- _vm->_font->setColors(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK);
- else
- _vm->_font->setColors(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK);
+void MadsScene::updateState() {
+ if (!_abortTimers && !_madsVm->_player._unk3) {
+ if (_dynamicHotspots._changed)
+ _dynamicHotspots.refresh();
- _vm->_font->setFont(FONT_MAIN_MADS);
- _vm->_font->writeString(this, sStatusText, (width() - _vm->_font->getWidth(sStatusText)) / 2, 142, 0);
+// int v = (_madsVm->_player._stepEnabled && !_action._startWalkFlag && !_abortTimers2) ? 1 : 0;
+// _screenObjects.check(v, false);
}
- //***DEBUG***
- _spriteSlots.getSprite(0).getFrame(1)->copyTo(this, 120, 90, 0);
-}
+ // Handle starting off any selected action
+ bool doPreAction = false;
+ if ((_action._selectedAction != 0) && _madsVm->_player._stepEnabled &&
+ !_action._startWalkFlag && !_abortTimers && !_madsVm->_player._unk3) {
+ // Start the action
+ _action.startAction();
-void MadsScene::updateState() {
- _sequenceList.tick();
+ if (_action._action.verbId == kVerbLookAt) {
+ _action._action.verbId = kVerbLook;
+ _action._savedFields.selectedRow = 0;
+ }
+ doPreAction = true;
+ }
+ if (doPreAction || ((_abortTimers != 0) && (_abortTimersMode == ABORTMODE_2)))
+ doPreactions();
+
+ checkStartWalk();
+
+ if (_action._inProgress && !_madsVm->_player._moving && !_action._startWalkFlag &&
+ (_madsVm->_player._newDirection == _madsVm->_player._direction)) {
+ // Reached the end of action movement, so ready to actually do action
+ doAction();
+ } else if ((_abortTimers != 0) && (_abortTimersMode == ABORTMODE_0))
+ // Do an action designated by scripts
+ doAction();
+
+ bool freeFlag = false;
+ if (_currentScene != _nextScene)
+ freeFlag = true;
+ else {
+ doSceneStep();
+
+ if (_currentScene != _nextScene)
+ freeFlag = true;
+ else {
+ // Update the player
+ _madsVm->_player.nextFrame();
+
+ // Handle updating the animation
+ if (!_abortTimers && (_activeAnimation))
+ _activeAnimation->update();
+
+ // Handle refreshing the mouse position display
+ if (_mouseMsgIndex != -1)
+ _madsVm->scene()->_kernelMessages.remove(_mouseMsgIndex);
+ if (_showMousePos) {
+ char buffer[20];
+ sprintf(buffer, "(%d,%d)", _madsVm->_mouse->currentPos().x, _madsVm->_mouse->currentPos().y);
+
+ _mouseMsgIndex = _madsVm->scene()->_kernelMessages.add(Common::Point(5, 5), 0x203, 0, 0, 1, buffer);
+ }
+ }
+ }
+ if (_madsVm->globals()->_config.easyMouse)
+ _action.refresh();
+
if ((_activeAnimation) && !_abortTimers) {
_activeAnimation->update();
- if (((MadsAnimation *) _activeAnimation)->freeFlag()) {
+ if (((MadsAnimation *) _activeAnimation)->freeFlag() || freeFlag) {
delete _activeAnimation;
_activeAnimation = NULL;
}
}
- _kernelMessages.update();
+ MadsView::update();
+
+ // Remove the animation if it's been completed
+ if ((_activeAnimation) && ((MadsAnimation *)_activeAnimation)->freeFlag())
+ freeAnimation();
+
+ if ((_action._selectedAction != 0) || !_madsVm->_player._stepEnabled) {
+ _action.clear();
+ _action._selectedAction = 0;
+ }
}
-int MadsScene::loadSceneSpriteSet(const char *setName) {
- char resName[100];
- strcpy(resName, setName);
+void MadsScene::checkStartWalk() {
+ if (_action._startWalkFlag && _action._walkFlag) {
+ _madsVm->_player.setDest(_destPos.x, _destPos.y, _destFacing);
+ _action._startWalkFlag = false;
+ }
+}
- // Append a '.SS' if it doesn't alreayd have an extension
- if (!strchr(resName, '.'))
- strcat(resName, ".SS");
+void MadsScene::doPreactions() {
+ if ((_screenObjects._v832EC == 0) || (_screenObjects._v832EC == 2)) {
+ _abortTimersMode2 = ABORTMODE_2;
+ _action.checkAction();
- return _spriteSlots.addSprites(resName);
+ _sceneLogic.doPreactions();
+
+ if (_abortTimersMode == ABORTMODE_2)
+ _abortTimers = 0;
+ }
}
-void MadsScene::loadPlayerSprites(const char *prefix) {
- const char suffixList[8] = { '8', '9', '6', '3', '2', '7', '4', '1' };
- char setName[80];
+void MadsScene::doSceneStep() {
+ // Step through the scene
+ _sceneLogic.doSceneStep();
- strcpy(setName, "*");
- strcat(setName, prefix);
- strcat(setName, "_0.SS");
- char *digitP = strchr(setName, '_') + 1;
+ _madsVm->_player.step();
+ _madsVm->_player._unk3 = 0;
- for (int idx = 0; idx < 8; ++idx) {
- *digitP = suffixList[idx];
+ if (_abortTimersMode == ABORTMODE_1)
+ _abortTimers = 0;
+}
- if (_vm->res()->resourceExists(setName)) {
- loadSceneSpriteSet(setName);
- return;
- }
+void MadsScene::doAction() {
+ warning("TODO MadsScene::doAction");
+}
+
+
+/**
+ * Does extra work at cleaning up the animation, and then deletes it
+ */
+void MadsScene::freeAnimation() {
+ if (!_activeAnimation)
+ return;
+
+ MadsAnimation *anim = (MadsAnimation *)_activeAnimation;
+ if (anim->freeFlag()) {
+ _madsVm->scene()->_spriteSlots.clear();
+ _madsVm->scene()->_spriteSlots.fullRefresh();
+ _madsVm->scene()->_sequenceList.scan();
}
- error("Couldn't find player sprites");
+ if (_madsVm->_player._visible) {
+ _madsVm->_player._forceRefresh = true;
+ _madsVm->_player.update();
+ }
+
+ delete _activeAnimation;
+ _activeAnimation = NULL;
+}
+
+
+int MadsScene::loadSceneSpriteSet(const char *setName) {
+ char resName[100];
+ strcpy(resName, setName);
+
+ // Append a '.SS' if it doesn't alreayd have an extension
+ if (!strchr(resName, '.'))
+ strcat(resName, ".SS");
+
+ return _spriteSlots.addSprites(resName);
}
enum boxSprites {
@@ -446,274 +566,676 @@ void MadsScene::showMADSV2TextBox(char *text, int x, int y, char *faceName) {
boxSprites->getFrame(bottomRight)->copyTo(_backgroundSurface, curX, curY + 1);
}
+void MadsScene::loadAnimation(const Common::String &animName, int abortTimers) {
+ if (_activeAnimation)
+ error("Multiple active animations are not allowed");
+
+ MadsAnimation *anim = new MadsAnimation(_vm, this);
+ anim->load(animName.c_str(), abortTimers);
+ _activeAnimation = anim;
+}
+
+bool MadsScene::getDepthHighBit(const Common::Point &pt) {
+ const byte *p = _depthSurface->getBasePtr(pt.x, pt.y);
+ if (_sceneResources._depthStyle == 2)
+ return ((*p << 4) & 0x80) != 0;
+
+ return (*p & 0x80) != 0;
+}
+
+bool MadsScene::getDepthHighBits(const Common::Point &pt) {
+ if (_sceneResources._depthStyle == 2)
+ return 0;
+
+ const byte *p = _depthSurface->getBasePtr(pt.x, pt.y);
+ return (*p & 0x70) >> 4;
+}
+
/*--------------------------------------------------------------------------*/
-MadsAction::MadsAction() {
- clear();
-}
-
-void MadsAction::clear() {
- _actionMode = ACTMODE_NONE;
- _actionMode2 = ACTMODE2_0;
- _word_86F42 = 0;
- _word_86F4E = 0;
- _articleNumber = 0;
- _lookFlag = false;
- _word_86F4A = 0;
- _statusText[0] = '\0';
- _selectedRow = -1;
- _currentHotspot = -1;
- _word_86F3A = -1;
- _word_86F4C = -1;
- //word_86F3A/word_86F4C
- _currentAction = kVerbNone;
- _objectNameId = -1;
- _objectDescId = -1;
- _word_83334 = -1;
-}
-
-void MadsAction::appendVocab(int vocabId, bool capitalise) {
- char *s = _statusText + strlen(_statusText);
- const char *vocabStr = _madsVm->globals()->getVocab(vocabId);
- strcpy(s, vocabStr);
- if (capitalise)
- *s = toupper(*s);
-
- strcat(s, " ");
-}
-
-void MadsAction::set() {
- int hotspotCount = _madsVm->scene()->getSceneResources().hotspotCount;
- bool flag = false;
- _currentAction = -1;
- _objectNameId = -1;
- _objectDescId = -1;
-
- if (_actionMode == ACTMODE_TALK) {
- // Handle showing the conversation selection. Rex at least doesn't actually seem to use this
- if (_selectedRow >= 0) {
- const char *desc = _madsVm->_converse[_selectedRow].desc;
- if (desc)
- strcpy(_statusText, desc);
- }
- } else if (_lookFlag && (_selectedRow == 0)) {
- // Two 'look' actions in succession, so the action becomes 'Look around'
- strcpy(_statusText, lookAroundStr);
+void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface) {
+ char buffer1[80];
+ const char *sceneName;
+
+ // TODO: Initialise spriteSet / xp_list
+
+ if (sceneNumber > 0) {
+ sceneName = MADSResourceManager::getResourceName(RESPREFIX_RM, sceneNumber, ".DAT");
} else {
- if ((_actionMode == ACTMODE_OBJECT) && (_selectedRow >= 0) && (_flags1 == 2) && (_flags2 == 0)) {
- // Use/to action
- int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject();
- MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject);
-
- _objectNameId = objEntry->descId;
- _currentAction = objEntry->vocabList[_selectedRow].vocabId;
-
- // Set up the status text stirng
- strcpy(_statusText, useStr);
- appendVocab(_objectNameId);
- strcpy(_statusText, toStr);
- appendVocab(_currentAction);
- } else {
- // Handling for if an action has been selected
- if (_selectedRow >= 0) {
- if (_actionMode == ACTMODE_VERB) {
- // Standard verb action
- _currentAction = verbList[_selectedRow].verb;
- } else {
- // Selected action on an inventory object
- int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject();
- MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject);
+ strcpy(buffer1, "*");
+ strcat(buffer1, resName);
+ sceneName = buffer1; // TODO: Check whether this needs to be converted to 'HAG form'
+ }
- _currentAction = objEntry->vocabList[_selectedRow].vocabId;
- }
+ Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneName);
+ MadsPack sceneInfo(rawStream);
- appendVocab(_currentAction, true);
+ // Chunk 0:
+ // Basic scene info
+ Common::SeekableReadStream *stream = sceneInfo.getItemStream(0);
- if (_currentAction == kVerbLook) {
- // Add in the word 'add'
- strcat(_statusText, atStr);
- strcat(_statusText, " ");
- }
- }
+ if (_vm->getGameType() == GType_RexNebular) {
+ int resSceneId = stream->readUint16LE();
+ assert(resSceneId == sceneNumber);
+ } else {
+ char roomFilename[10];
+ char roomFilenameExpected[10];
+ sprintf(roomFilenameExpected, "*RM%d", sceneNumber);
- // Handling for if a hotspot has been selected/highlighted
- if ((_currentHotspot >= 0) && (_selectedRow >= 0) && (_articleNumber > 0) && (_flags1 == 2)) {
- flag = true;
+ stream->read(roomFilename, 6);
+ roomFilename[6] = 0;
+ assert(!strcmp(roomFilename, roomFilenameExpected));
+ }
- strcat(_statusText, englishMADSArticleList[_articleNumber]);
- strcat(_statusText, " ");
- }
+ // TODO: The following is wrong for Phantom/Dragon
+ _artFileNum = stream->readUint16LE();
+ _depthStyle = stream->readUint16LE();
+ _width = stream->readUint16LE();
+ _height = stream->readUint16LE();
+
+ stream->skip(24);
- if (_currentHotspot >= 0) {
- if (_selectedRow < 0) {
- int verbId;
-
- if (_currentHotspot < hotspotCount) {
- // Get the verb Id from the hotspot
- verbId = (*_madsVm->scene()->getSceneResources().hotspots)[_currentHotspot].getVerbID();
- } else {
- // Get the verb Id from the scene object
- verbId = (*_madsVm->scene()->getSceneResources().props)[_currentHotspot - hotspotCount].getVerbID();
- }
-
- if (verbId > 0) {
- // Set the specified action
- _currentAction = verbId;
- appendVocab(_currentAction, true);
- } else {
- // Default to a standard 'walk to'
- _currentAction = kVerbWalkTo;
- strcat(_statusText, walkToStr);
- }
- }
+ int nodeCount = stream->readUint16LE();
+ _yBandsEnd = stream->readUint16LE();
+ _yBandsStart = stream->readUint16LE();
+ _maxScale = stream->readSint16LE();
+ _minScale = stream->readSint16LE();
+ for (int i = 0; i < DEPTH_BANDS_SIZE; ++i)
+ _depthBands[i] = stream->readUint16LE();
+ stream->skip(2);
+
+ // Load in any scene objects
+ for (int i = 0; i < nodeCount; ++i) {
+ SceneNode rec;
+ rec.load(stream);
+ _nodes.push_back(rec);
+ }
+ for (int i = 0; i < 20 - nodeCount; ++i)
+ stream->skip(48);
- if ((_actionMode2 == ACTMODE2_2) || (_actionMode2 == ACTMODE2_5)) {
- // Get name from given inventory object
- int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_currentHotspot);
- _objectNameId = _madsVm->globals()->getObject(objectId)->descId;
- } else if (_currentHotspot < hotspotCount) {
- // Get name from scene hotspot
- _objectNameId = (*_madsVm->scene()->getSceneResources().hotspots)[_currentHotspot].getVocabID();
- } else {
- // Get name from temporary scene hotspot
- _objectNameId = (*_madsVm->scene()->getSceneResources().props)[_currentHotspot].getVocabID();
+ // Add two extra nodes in that will be used for player movement
+ for (int i = 0; i < 2; ++i) {
+ SceneNode rec;
+ _nodes.push_back(rec);
+ }
+
+ int setCount = stream->readUint16LE();
+ stream->readUint16LE();
+ for (int i = 0; i < setCount; ++i) {
+ char buffer2[64];
+ Common::String s(buffer2, 64);
+ _setNames.push_back(s);
+ }
+
+ delete stream;
+
+ // Initialise a copy of the surfaces if they weren't provided
+ bool dsFlag = false, ssFlag = false;
+ if (!surface) {
+ surface = new M4Surface(_width, _height);
+ ssFlag = true;
+ } else if ((_width != surface->width()) || (_height != surface->height()))
+ surface->setSize(_width, _height);
+
+ if (!depthSurface) {
+ depthSurface = new M4Surface(_width, _height);
+ dsFlag = true;
+ } else if ((_width != depthSurface->width()) || (_height != depthSurface->height()))
+ depthSurface->setSize(_width, _height);
+
+
+ // For Rex Nebular, read in the scene's compressed walk surface information
+ if (_vm->getGameType() == GType_RexNebular) {
+ assert(depthSurface);
+ stream = sceneInfo.getItemStream(1);
+ byte *walkData = (byte *)malloc(stream->size());
+ stream->read(walkData, stream->size());
+
+ // For Rex Nebular, the walk areas are part of the scene info
+ byte *destP = depthSurface->getBasePtr(0, 0);
+ const byte *srcP = walkData;
+ byte runLength;
+
+ // Run length encoded depth data
+ while ((runLength = *srcP++) != 0) {
+ if (_depthStyle == 2) {
+ // 2-bit depth pixels
+ byte byteVal = *srcP++;
+ for (int byteCtr = 0; byteCtr < runLength; ++byteCtr) {
+ byte v = byteVal;
+ for (int bitCtr = 0; bitCtr < 4; ++bitCtr, v >>= 2)
+ *destP++ = (((v & 1) + 1) << 3) - 1;
}
+ } else {
+ // 8-bit depth pixels
+ Common::set_to(destP, destP + runLength, *srcP++);
+ destP += runLength;
}
}
- if ((_currentHotspot >= 0) && (_articleNumber > 0) && !flag) {
- if (_articleNumber == -1) {
- if (_word_86F3A >= 0) {
- int articleNum = 0;
+ free(walkData);
+ delete stream;
+ }
- if ((_word_86F42 == 2) || (_word_86F42 == 5)) {
- int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_currentHotspot);
- articleNum = _madsVm->globals()->getObject(objectId)->article;
- } else if (_word_86F3A < hotspotCount) {
- articleNum = (*_madsVm->scene()->getSceneResources().hotspots)[_currentHotspot].getArticle();
- } else {
+ _vm->_resourceManager->toss(sceneName);
- }
- }
+ // Load the surface artwork
+ surface->loadBackground(_artFileNum);
- } else if ((_articleNumber == kVerbLook) || (_vm->getGameType() != GType_RexNebular) ||
- (strcmp(_madsVm->globals()->getVocab(_objectDescId), fenceStr) != 0)) {
- // Write out the article
- strcat(_statusText, englishMADSArticleList[_articleNumber]);
- } else {
- // Special case for a 'fence' entry in Rex Nebular
- strcat(_statusText, overStr);
- }
+ // Final cleanup
+ if (ssFlag)
+ delete surface;
+ if (dsFlag)
+ delete depthSurface;
+}
+
+void MadsSceneResources::setRouteNode(int nodeIndex, const Common::Point &pt, M4Surface *depthSurface) {
+ int flags, hypotenuse;
+
+ _nodes[nodeIndex].pt = pt;
+
+ // Recalculate inter-node lengths
+ for (uint idx = 0; idx < _nodes.size(); ++idx) {
+ int entry;
+ if (idx == (uint)nodeIndex) {
+ entry = 0x3FFF;
+ } else {
+ // Process the node
+ flags = getRouteFlags(pt, _nodes[idx].pt, depthSurface);
+
+ int xDiff = ABS(_nodes[idx].pt.x - pt.x);
+ int yDiff = ABS(_nodes[idx].pt.y - pt.y);
+ hypotenuse = SqrtF16(xDiff * xDiff + yDiff * yDiff);
- strcat(_statusText, " ");
+ if (hypotenuse >= 0x3FFF)
+ // Shouldn't ever be this large
+ hypotenuse = 0x3FFF;
+
+ entry = hypotenuse | flags;
+ _nodes[idx].indexes[nodeIndex] = entry;
+ _nodes[nodeIndex].indexes[idx] = entry;
+ }
+ }
+}
+
+int MadsSceneResources::getRouteFlags(const Common::Point &src, const Common::Point &dest, M4Surface *depthSurface) {
+ int result = 0x8000;
+ bool flag = false;
+
+ int xDiff = ABS(dest.x - src.x);
+ int yDiff = ABS(dest.y - src.y);
+ int xDirection = dest.x >= src.x ? 1 : -1;
+ int yDirection = dest.y >= src.y ? depthSurface->width() : -depthSurface->width();
+ int majorDiff = 0;
+ if (dest.x < src.x)
+ majorDiff = MAX(xDiff, yDiff);
+ ++xDiff;
+ ++yDiff;
+
+ byte *srcP = depthSurface->getBasePtr(src.x, src.y);
+
+ int totalCtr = majorDiff;
+ for (int xCtr = 0; xCtr < xDiff; ++xCtr, srcP += xDirection) {
+ totalCtr += yDiff;
+
+ if ((*srcP & 0x80) == 0)
+ flag = false;
+ else if (!flag) {
+ flag = true;
+ result -= 0x4000;
+ if (result == 0)
+ break;
}
- // Append object description if necessary
- if (_word_86F3A >= 0)
- appendVocab(_objectDescId);
+ while (totalCtr >= xDiff) {
+ totalCtr -= xDiff;
- // Remove any trailing space character
- int statusLen = strlen(_statusText);
- if ((statusLen > 0) && (_statusText[statusLen - 1] == ' '))
- _statusText[statusLen - 1] = '\0';
+ if ((*srcP & 0x80) == 0)
+ flag = false;
+ else if (!flag) {
+ flag = true;
+ result -= 0x4000;
+ if (result == 0)
+ break;
+ }
+
+ srcP += yDirection;
+ }
+ if (result == 0)
+ break;
}
- _word_83334 = -1;
+ return result;
}
/*--------------------------------------------------------------------------*/
-void MadsSceneResources::load(int sId) {
- const char *sceneInfoStr = MADSResourceManager::getResourceName(RESPREFIX_RM, sId, ".DAT");
- Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneInfoStr);
- MadsPack sceneInfo(rawStream);
+/*--------------------------------------------------------------------------
+ * MadsInterfaceView handles the user interface section at the bottom of
+ * game screens in MADS games
+ *--------------------------------------------------------------------------
+ */
- // Basic scene info
- Common::SeekableReadStream *stream = sceneInfo.getItemStream(0);
+MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm,
+ Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) {
+ _screenType = VIEWID_INTERFACE;
+ _highlightedElement = -1;
+ _topIndex = 0;
+ _selectedObject = -1;
+ _cheatKeyCtr = 0;
+
+ _objectSprites = NULL;
+ _objectPalData = NULL;
+
+ /* Set up the rect list for screen elements */
+ // Actions
+ for (int i = 0; i < 10; ++i)
+ _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2,
+ ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2);
+
+ // Scroller elements (up arrow, scroller, down arrow)
+ _screenObjects.addRect(73, 160, 82, 167);
+ _screenObjects.addRect(73, 168, 82, 190);
+ _screenObjects.addRect(73, 191, 82, 198);
+
+ // Inventory object names
+ for (int i = 0; i < 5; ++i)
+ _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8);
+
+ // Full rectangle area for all vocab actions
+ for (int i = 0; i < 5; ++i)
+ _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8);
+}
- int resSceneId = stream->readUint16LE();
- assert(resSceneId == sId);
+MadsInterfaceView::~MadsInterfaceView() {
+ delete _objectSprites;
+}
- artFileNum = stream->readUint16LE();
- field_4 = stream->readUint16LE();
- width = stream->readUint16LE();
- height = stream->readUint16LE();
- assert((width == 320) && (height == 156));
-
- stream->skip(24);
+void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) {
+ switch (newMode) {
+ case ITEM_NORMAL:
+ _vm->_font->current()->setColours(4, 4, 0xff);
+ break;
+ case ITEM_HIGHLIGHTED:
+ _vm->_font->current()->setColours(5, 5, 0xff);
+ break;
+ case ITEM_SELECTED:
+ _vm->_font->current()->setColours(6, 6, 0xff);
+ break;
+ }
+}
- objectCount = stream->readUint16LE();
+void MadsInterfaceView::initialise() {
+ // Build up the inventory list
+ _inventoryList.clear();
- stream->skip(40);
+ for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) {
+ MadsObject *obj = _madsVm->globals()->getObject(i);
+ if (obj->roomNumber == PLAYER_INVENTORY)
+ _inventoryList.push_back(i);
+ }
- for (int i = 0; i < objectCount; ++i) {
- objects[i].load(stream);
+ // If the inventory has at least one object, select it
+ if (_inventoryList.size() > 0)
+ setSelectedObject(_inventoryList[0]);
+}
+
+void MadsInterfaceView::setSelectedObject(int objectNumber) {
+ char resName[80];
+
+ // Load inventory resource
+ if (_objectSprites) {
+ _vm->_palette->deleteRange(_objectPalData);
+ delete _objectSprites;
}
- // For Rex Nebular, read in the scene's compressed walk surface information
- if (_vm->getGameType() == GType_RexNebular) {
- delete walkData;
+ // Check to make sure the object is in the inventory, and also visible on-screen
+ int idx = _inventoryList.indexOf(objectNumber);
+ if (idx == -1) {
+ // Object wasn't found, so return
+ _selectedObject = -1;
+ return;
+ }
- stream = sceneInfo.getItemStream(1);
- walkData = (byte *)malloc(stream->size());
- stream->read(walkData, stream->size());
+ // Found the object
+ if (idx < _topIndex)
+ _topIndex = idx;
+ else if (idx >= (_topIndex + 5))
+ _topIndex = MAX(0, idx - 4);
+
+ _selectedObject = objectNumber;
+ sprintf(resName, "*OB%.3dI.SS", objectNumber);
+
+ Common::SeekableReadStream *data = _vm->res()->get(resName);
+ _objectSprites = new SpriteAsset(_vm, data, data->size(), resName);
+ _vm->res()->toss(resName);
+
+ // Slot it into available palette space
+ _objectPalData = _objectSprites->getRgbList();
+ _vm->_palette->addRange(_objectPalData);
+ _objectSprites->translate(_objectPalData, true);
+
+ _objectFrameNumber = 0;
+}
+
+void MadsInterfaceView::addObjectToInventory(int objectNumber) {
+ if (_inventoryList.indexOf(objectNumber) == -1) {
+ _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY;
+ _inventoryList.push_back(objectNumber);
}
- _vm->_resourceManager->toss(sceneInfoStr);
+ setSelectedObject(objectNumber);
}
-/*--------------------------------------------------------------------------*/
+void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) {
+ _vm->_font->setFont(FONT_INTERFACE_MADS);
+ char buffer[100];
+
+ // Check to see if any dialog is currently active
+ bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL;
+
+ // Highlighting logic for action list
+ int actionIndex = 0;
+ for (int x = 0; x < 2; ++x) {
+ for (int y = 0; y < 5; ++y, ++actionIndex) {
+ // Determine the font colour depending on whether an item is selected. Note that the first action,
+ // 'Look', is always 'selected', even when another action is clicked on
+ setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED :
+ ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL));
+
+ // Get the verb action and capitalise it
+ const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex);
+ strcpy(buffer, verbStr);
+ if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
+
+ // Display the verb
+ const Common::Rect r(_screenObjects[actionIndex]);
+ _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
+ }
+ }
-/**
- * Adds a new entry to the timed on-screen text display list
- */
-/*
-void MadsScreenText::draw(M4Surface *surface) {
+ // Check for highlighting of the scrollbar controls
+ if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) {
+ // Highlight the control's borders
+ const Common::Rect r(_screenObjects[_highlightedElement]);
+ destSurface->frameRect(r, 5);
+ }
+
+ // Draw the horizontal line in the scroller representing the current top selected
+ const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]);
+ int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1);
+ destSurface->setColor(4);
+ destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP);
+
+ // List inventory items
+ for (uint i = 0; i < 5; ++i) {
+ if ((_topIndex + i) >= _inventoryList.size())
+ break;
+
+ const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject(
+ _inventoryList[_topIndex + i])->descId);
+ strcpy(buffer, descStr);
+ if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
+
+ const Common::Rect r(_screenObjects[INVLIST_START + i]);
+
+ // Set the highlighting of the inventory item
+ if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED);
+ else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED);
+ else setFontMode(ITEM_NORMAL);
+
+ // Write out it's description
+ _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
+ }
+
+ // Handle the display of any currently selected object
+ if (_objectSprites) {
+ // Display object sprite. Note that the frame number isn't used directly, because it would result
+ // in too fast an animation
+ M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED);
+ spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, TRANSPARENT_COLOUR_INDEX);
+
+ if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) {
+ // If objects need to be animated, move to the next frame
+ if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED))
+ _objectFrameNumber = 0;
+ }
+
+ // List the vocab actions for the currently selected object
+ MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
+ int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
+
+ for (int i = 0; i < obj->vocabCount; ++i) {
+ const Common::Rect r(_screenObjects[VOCAB_START + i]);
+
+ // Get the vocab description and capitalise it
+ const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId);
+ strcpy(buffer, descStr);
+ if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
+
+ // Set the highlighting and display the entry
+ setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL);
+ _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
+ }
+ }
}
-void MadsScreenText::timedDisplay() {
- for (int idx = 0; !_abortTimedText && (idx < OLD_TEXT_DISPLAY_SIZE); ++idx) {
- if (((_timedText[idx].flags & TEXTFLAG_ACTIVE) != 0) &&
- (_timedText[idx].frameTimer <= g_system->getMillis()))
- // Add the specified entry
- addTimedText(&_timedText[idx]);
+bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) {
+ MadsAction &act = _madsVm->scene()->_action;
+
+ // If the mouse isn't being held down, then reset the repeated scroll timer
+ if (eventType != MEVENT_LEFT_HOLD)
+ _nextScrollerTicks = 0;
+
+ // Handle various event types
+ switch (eventType) {
+ case MEVENT_MOVE:
+ // If the cursor isn't in "wait mode", don't do any processing
+ if (_vm->_mouse->getCursorNum() == CURSOR_WAIT)
+ return true;
+
+ // Ensure the cursor is the standard arrow
+ _vm->_mouse->setCursorNum(CURSOR_ARROW);
+
+ // Check if any interface element is currently highlighted
+ _highlightedElement = _screenObjects.find(Common::Point(x, y));
+
+ return true;
+
+ case MEVENT_LEFT_CLICK:
+ // Left mouse click
+ {
+ // Check if an inventory object was selected
+ if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) {
+ // Ensure there is an inventory item listed in that cell
+ uint idx = _highlightedElement - INVLIST_START;
+ if ((_topIndex + idx) < _inventoryList.size()) {
+ // Set the selected object
+ setSelectedObject(_inventoryList[_topIndex + idx]);
+ }
+ } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) {
+ // A standard action was selected
+ int verbId = kVerbLook + (_highlightedElement - ACTIONS_START);
+ warning("Selected action #%d", verbId);
+
+ } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) {
+ // A vocab action was selected
+ MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
+ int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
+ if (vocabIndex >= 0) {
+ act._actionMode = ACTMODE_OBJECT;
+ act._actionMode2 = ACTMODE2_2;
+ act._flags1 = obj->vocabList[1].flags1;
+ act._flags2 = obj->vocabList[1].flags2;
+
+ act._action.verbId = _selectedObject;
+ act._articleNumber = act._flags2;
+ }
+ }
+ }
+ return true;
+
+ case MEVENT_LEFT_HOLD:
+ // Left mouse hold
+ // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down
+ if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) {
+ if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) {
+ // Handle scroll up/down action
+ _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY;
+
+ if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0))
+ --_topIndex;
+ if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1)))
+ ++_topIndex;
+ }
+ }
+ return true;
+
+ case MEVENT_LEFT_DRAG:
+ // Left mouse drag
+ // Handle the the the scroller area that can be dragged to adjust the top displayed index
+ if (_highlightedElement == SCROLL_SCROLLER) {
+ // Calculate the new top index based on the Y position
+ const Common::Rect r(_screenObjects[SCROLL_SCROLLER]);
+ _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5),
+ 0, (int)_inventoryList.size() - 1);
+ }
+ return true;
+
+ case KEVENT_KEY:
+ if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
+ handleCheatKey(param1);
+ handleKeypress(param1);
+ return true;
+
+ default:
+ break;
}
+
+ return false;
}
-void MadsScreenText::addTimedText(TimedText *entry) {
- if ((entry->flags & TEXTFLAG_40) != 0) {
- this->setActive2(entry->textDisplayIndex);
- entry->flags &= 0x7F;
- return;
+bool MadsInterfaceView::handleCheatKey(int32 keycode) {
+ switch (keycode) {
+ case Common::KEYCODE_SPACE:
+ // TODO: Move player to current destination
+ return true;
+
+ case Common::KEYCODE_c | (Common::KBD_CTRL << 24):
+ // Toggle display of mouse position
+ _madsVm->scene()->_showMousePos = !_madsVm->scene()->_showMousePos;
+ break;
+
+ case Common::KEYCODE_t | (Common::KEYCODE_LALT << 24):
+ case Common::KEYCODE_t | (Common::KEYCODE_RALT << 24):
+ {
+ // Teleport to room
+ //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE);
+
+
+ return true;
}
- if ((entry->flags & TEXTFLAG_8) == 0)
- // FIXME: Adjust timeouts for ScumVM's milli counter
- entry->timeout -= 3;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+const char *CHEAT_SEQUENCE = "widepipe";
- if ((entry->flags & TEXTFLAG_4) != 0) {
- Text4A &rec = _text4A[entry->unk4AIndex];
- if ((rec.field25 != 0) || (rec.active == 0))
- entry->timeout = 0;
+bool MadsInterfaceView::handleKeypress(int32 keycode) {
+ int flags = keycode >> 24;
+ int kc = keycode & 0xffff;
+
+ // Capitalise the letter if necessary
+ if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) {
+ if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) {
+ ++_cheatKeyCtr;
+ if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
+ Dialog::display(_vm, 22, cheatingEnabledDesc);
+ return true;
+ } else {
+ _cheatKeyCtr = 0;
+ }
}
- if ((entry->timeout == 0) && !_abortTimedText) {
- entry->flags |= TEXTFLAG_40;
+ // Handle the various keys
+ if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) {
+ // Game menu
+ _madsVm->globals()->dialogType = DIALOG_GAME_MENU;
+ leaveScene();
+ return false;
+ } else if (flags & Common::KBD_CTRL) {
+ // Handling of the different control key combinations
+ switch (kc) {
+ case Common::KEYCODE_i:
+ // Mouse to inventory
+ warning("TODO: Mouse to inventory");
+ break;
+
+ case Common::KEYCODE_k:
+ // Toggle hotspots
+ warning("TODO: Toggle hotspots");
+ break;
+
+ case Common::KEYCODE_p:
+ // Player stats
+ warning("TODO: Player stats");
+ break;
+
+ case Common::KEYCODE_q:
+ // Quit game
+ break;
+
+ case Common::KEYCODE_s:
+ // Activate sound
+ warning("TODO: Activate sound");
+ break;
+
+ case Common::KEYCODE_t:
+ // Rotate player - This was Ctrl-U in the original, but in ScummVM Ctrl-U is a global mute key
+ _madsVm->_player._newDirection = _madsVm->_player._directionListIndexes[_madsVm->_player._newDirection + 10];
+ break;
+
+ case Common::KEYCODE_v: {
+ // Release version
+ Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr);
+ _vm->_viewManager->addView(dlg);
+ _vm->_viewManager->moveToFront(dlg);
+ return false;
+ }
- if (entry->field_1C) {
- _abortTimedText = entry->field_1C;
- //word_84208 = entry->field_1D;
-
- if (entry->field_1D != 1) {
- // Restore the action list
- for (int i = 0; i < 3; ++i)
- _madsVm->scene()->actionNouns[i] = entry->actionNouns[i];
- }
+ default:
+ break;
+ }
+ } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) {
+ // Quit Game
+
+ } else {
+ // Standard keypresses
+ switch (kc) {
+ case Common::KEYCODE_F2:
+ // Save game
+ _madsVm->globals()->dialogType = DIALOG_SAVE;
+ leaveScene();
+ break;
+ case Common::KEYCODE_F3:
+ // Restore game
+ _madsVm->globals()->dialogType = DIALOG_RESTORE;
+ leaveScene();
+ break;
}
}
+//DIALOG_OPTIONS
+ return false;
+}
- // TODO: code from 'loc_244ec' onwards
+void MadsInterfaceView::leaveScene() {
+ // Close the scene
+ View *view = _madsVm->_viewManager->getView(VIEWID_SCENE);
+ _madsVm->_viewManager->deleteView(view);
}
-*/
} // End of namespace M4
diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h
index f7625bb761..7723058cc7 100644
--- a/engines/m4/mads_scene.h
+++ b/engines/m4/mads_scene.h
@@ -33,96 +33,81 @@
namespace M4 {
#define INTERFACE_HEIGHT 106
+class MadsInterfaceView;
-struct SpriteSlot {
- int16 spriteId;
- int16 scale;
- uint16 spriteListIndex;
-};
+#define DEPTH_BANDS_SIZE 15
+#define MAX_ROUTE_NODES 22
+
+enum ScreenCategory {CAT_NONE = 0, CAT_ACTION = 1, CAT_INV_LIST = 2, CAT_INV_VOCAB, CAT_HOTSPOT = 4,
+ CAT_INV_ANIM = 6, CAT_6, CAT_INV_SCROLLER = 7, CAT_12 = 12};
+
+class SceneNode {
+public:
+ Common::Point pt;
+ int indexes[MAX_ROUTE_NODES];
-struct DirtyArea {
bool active;
- bool active2;
- Common::Rect bounds;
-};
+ SceneNode() {
+ active = false;
+ }
-class MadsSceneResources: public SceneResources {
-public:
- int sceneId;
- int artFileNum;
- int field_4;
- int width;
- int height;
-
- int objectCount;
- MadsObject objects[32];
-
- int walkSize;
- byte *walkData;
-
- MadsSceneResources() { walkSize = 0; walkData = NULL; }
- ~MadsSceneResources() { delete walkData; }
- void load(int sceneId);
+ void load(Common::SeekableReadStream *stream);
};
-enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6};
-enum MAdsActionMode2 {ACTMODE2_0 = 0, ACTMODE2_2 = 2, ACTMODE2_5 = 5};
+typedef Common::Array<SceneNode> SceneNodeList;
-class MadsAction {
+class MadsSceneResources: public SceneResources {
private:
- char _statusText[100];
-
- void appendVocab(int vocabId, bool capitalise = false);
-public:
- int _currentHotspot;
- int _objectNameId;
- int _objectDescId;
- int _currentAction;
- int8 _flags1, _flags2;
- MadsActionMode _actionMode;
- MAdsActionMode2 _actionMode2;
- int _articleNumber;
- bool _lookFlag;
- int _selectedRow;
- // Unknown fields
- int16 _word_86F3A;
- int16 _word_86F42;
- int16 _word_86F4E;
- int16 _word_86F4A;
- int16 _word_83334;
- int16 _word_86F4C;
-
+ int getRouteFlags(const Common::Point &src, const Common::Point &dest, M4Surface *depthSurface);
public:
- MadsAction();
-
- void clear();
- void set();
- const char *statusText() const { return _statusText; }
+ int _sceneId;
+ int _artFileNum;
+ int _depthStyle;
+ int _width;
+ int _height;
+ SceneNodeList _nodes;
+ Common::Array<Common::String> _setNames;
+ int _yBandsStart, _yBandsEnd;
+ int _maxScale, _minScale;
+ int _depthBands[DEPTH_BANDS_SIZE];
+
+ MadsSceneResources() {}
+ ~MadsSceneResources() {}
+ void load(int sceneId, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface);
+ int bandsRange() const { return _yBandsEnd - _yBandsStart; }
+ int scaleRange() const { return _maxScale - _minScale; }
+ void setRouteNode(int nodeIndex, const Common::Point &pt, M4Surface *depthSurface);
};
-#define DIRTY_AREA_SIZE 90
-
class MadsScene : public Scene, public MadsView {
private:
MadsEngine *_vm;
MadsSceneResources _sceneResources;
- MadsAction _action;
+ Animation *_activeAnimation;
MadsSceneLogic _sceneLogic;
SpriteAsset *_playerSprites;
- DirtyArea _dirtyAreas[DIRTY_AREA_SIZE];
+ int _mouseMsgIndex;
+ int _highlightedHotspot;
void drawElements();
- void loadScene2(const char *aaName);
+ void loadScene2(const char *aaName, int sceneNumber);
void loadSceneTemporary();
void loadSceneHotspots(int sceneNumber);
void clearAction();
void appendActionVocab(int vocabId, bool capitalise);
void setAction();
+ void checkStartWalk();
+ void doPreactions();
+ void doSceneStep();
+ void doAction();
public:
char _aaName[100];
- uint16 actionNouns[3];
+ bool _showMousePos;
+ Common::Point _destPos;
+ int _destFacing;
+ Common::Point _customDest;
public:
MadsScene(MadsEngine *vm);
virtual ~MadsScene();
@@ -132,7 +117,7 @@ public:
virtual void leaveScene();
virtual void loadSceneCodes(int sceneNumber, int index = 0);
virtual void show();
- virtual void checkHotspotAtMousePos(int x, int y);
+ virtual void mouseMove(int x, int y);
virtual void leftClick(int x, int y);
virtual void rightClick(int x, int y);
virtual void setAction(int action, int objectId = -1);
@@ -141,13 +126,65 @@ public:
virtual void updateState();
int loadSceneSpriteSet(const char *setName);
- void loadPlayerSprites(const char *prefix);
void showMADSV2TextBox(char *text, int x, int y, char *faceName);
+ void loadAnimation(const Common::String &animName, int abortTimers);
+ Animation *activeAnimation() const { return _activeAnimation; }
+ void freeAnimation();
MadsInterfaceView *getInterface() { return (MadsInterfaceView *)_interfaceSurface; }
MadsSceneResources &getSceneResources() { return _sceneResources; }
- MadsAction &getAction() { return _action; }
- void setStatusText(const char *text) {}//***DEPRECATED***
+ bool getDepthHighBit(const Common::Point &pt);
+ bool getDepthHighBits(const Common::Point &pt);
+};
+
+#define CHEAT_SEQUENCE_MAX 8
+
+class IntegerList : public Common::Array<int> {
+public:
+ int indexOf(int v) {
+ for (uint i = 0; i < size(); ++i)
+ if (operator [](i) == v)
+ return i;
+ return -1;
+ }
+};
+
+enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED};
+
+enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12,
+ INVLIST_START = 13, VOCAB_START = 18};
+
+class MadsInterfaceView : public GameInterfaceView {
+private:
+ IntegerList _inventoryList;
+ RectList _screenObjects;
+ int _highlightedElement;
+ int _topIndex;
+ uint32 _nextScrollerTicks;
+ int _cheatKeyCtr;
+
+ // Object display fields
+ int _selectedObject;
+ SpriteAsset *_objectSprites;
+ RGBList *_objectPalData;
+ int _objectFrameNumber;
+
+ void setFontMode(InterfaceFontMode newMode);
+ bool handleCheatKey(int32 keycode);
+ bool handleKeypress(int32 keycode);
+ void leaveScene();
+public:
+ MadsInterfaceView(MadsM4Engine *vm);
+ ~MadsInterfaceView();
+
+ virtual void initialise();
+ virtual void setSelectedObject(int objectNumber);
+ virtual void addObjectToInventory(int objectNumber);
+ int getSelectedObject() { return _selectedObject; }
+ int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; }
+
+ void onRefresh(RectList *rects, M4Surface *destSurface);
+ bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents);
};
} // End of namespace M4
diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp
index e50c35bc04..3e5f0c2ac9 100644
--- a/engines/m4/mads_views.cpp
+++ b/engines/m4/mads_views.cpp
@@ -24,6 +24,7 @@
*/
#include "m4/m4_views.h"
+#include "m4/animation.h"
#include "m4/dialogs.h"
#include "m4/events.h"
#include "m4/font.h"
@@ -36,14 +37,324 @@
namespace M4 {
-static const int INV_ANIM_FRAME_SPEED = 2;
-static const int INVENTORY_X = 160;
-static const int INVENTORY_Y = 159;
-static const int SCROLLER_DELAY = 200;
+MadsAction::MadsAction(MadsView &owner): _owner(owner) {
+ clear();
+ _currentAction = kVerbNone;
+ _startWalkFlag = false;
+ _statusTextIndex = -1;
+ _selectedAction = 0;
+ _inProgress = false;
+}
+
+void MadsAction::clear() {
+ _v83338 = 1;
+ _actionMode = ACTMODE_NONE;
+ _actionMode2 = ACTMODE2_0;
+ _v86F42 = 0;
+ _v86F4E = 0;
+ _articleNumber = 0;
+ _lookFlag = false;
+ _v86F4A = 0;
+ _statusText[0] = '\0';
+ _selectedRow = -1;
+ _hotspotId = -1;
+ _v86F3A = -1;
+ _v86F4C = -1;
+ _action.verbId = -1;
+ _action.objectNameId = -1;
+ _action.indirectObjectId = -1;
+ _textChanged = true;
+ _walkFlag = false;
+}
+
+void MadsAction::appendVocab(int vocabId, bool capitalise) {
+ char *s = _statusText + strlen(_statusText);
+ const char *vocabStr = _madsVm->globals()->getVocab(vocabId);
+ strcpy(s, vocabStr);
+ if (capitalise)
+ *s = toupper(*s);
+
+ strcat(s, " ");
+}
+
+void MadsAction::set() {
+ int hotspotCount = _madsVm->scene()->getSceneResources().hotspots->size();
+ bool flag = false;
+ strcpy(_statusText, "");
+
+ _currentAction = -1;
+ _action.objectNameId = -1;
+ _action.indirectObjectId = -1;
+
+ if (_actionMode == ACTMODE_TALK) {
+ // Handle showing the conversation selection. Rex at least doesn't actually seem to use this
+ if (_selectedRow >= 0) {
+ const char *desc = _madsVm->_converse[_selectedRow].desc;
+ if (desc)
+ strcpy(_statusText, desc);
+ }
+ } else if (_lookFlag && (_selectedRow == 0)) {
+ // Two 'look' actions in succession, so the action becomes 'Look around'
+ strcpy(_statusText, lookAroundStr);
+ } else {
+ if ((_actionMode == ACTMODE_OBJECT) && (_selectedRow >= 0) && (_flags1 == 2) && (_flags2 == 0)) {
+ // Use/to action
+ int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject();
+ MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject);
+
+ _action.objectNameId = objEntry->descId;
+ _currentAction = objEntry->vocabList[_selectedRow].vocabId;
+
+ // Set up the status text stirng
+ strcpy(_statusText, useStr);
+ appendVocab(_action.objectNameId);
+ strcpy(_statusText, toStr);
+ appendVocab(_currentAction);
+ } else {
+ // Handling for if an action has been selected
+ if (_selectedRow >= 0) {
+ if (_actionMode == ACTMODE_VERB) {
+ // Standard verb action
+ _currentAction = verbList[_selectedRow].verb;
+ } else {
+ // Selected action on an inventory object
+ int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject();
+ MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject);
+
+ _currentAction = objEntry->vocabList[_selectedRow].vocabId;
+ }
+
+ appendVocab(_currentAction, true);
+
+ if (_currentAction == kVerbLook) {
+ // Add in the word 'add'
+ strcat(_statusText, atStr);
+ strcat(_statusText, " ");
+ }
+ }
+
+ // Handling for if a hotspot has been selected/highlighted
+ if ((_hotspotId >= 0) && (_selectedRow >= 0) && (_articleNumber > 0) && (_flags1 == 2)) {
+ flag = true;
+
+ strcat(_statusText, englishMADSArticleList[_articleNumber]);
+ strcat(_statusText, " ");
+ }
+
+ if (_hotspotId >= 0) {
+ if (_selectedRow < 0) {
+ int verbId;
+
+ if (_hotspotId < hotspotCount) {
+ // Get the verb Id from the hotspot
+ verbId = (*_madsVm->scene()->getSceneResources().hotspots)[_hotspotId].getVerbID();
+ } else {
+ // Get the verb Id from the scene object
+ verbId = (*_madsVm->scene()->getSceneResources().dynamicHotspots)[_hotspotId - hotspotCount].getVerbID();
+ }
+
+ if (verbId > 0) {
+ // Set the specified action
+ _currentAction = verbId;
+ appendVocab(_currentAction, true);
+ } else {
+ // Default to a standard 'walk to'
+ _currentAction = kVerbWalkTo;
+ strcat(_statusText, walkToStr);
+ }
+ }
+
+ if ((_actionMode2 == ACTMODE2_2) || (_actionMode2 == ACTMODE2_5)) {
+ // Get name from given inventory object
+ int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_hotspotId);
+ _action.objectNameId = _madsVm->globals()->getObject(objectId)->descId;
+ } else if (_hotspotId < hotspotCount) {
+ // Get name from scene hotspot
+ _action.objectNameId = (*_madsVm->scene()->getSceneResources().hotspots)[_hotspotId].getVocabID();
+ } else {
+ // Get name from temporary scene hotspot
+ _action.objectNameId = (*_madsVm->scene()->getSceneResources().dynamicHotspots)[_hotspotId].getVocabID();
+ }
+ appendVocab(_action.objectNameId);
+ }
+ }
+
+ if ((_hotspotId >= 0) && (_articleNumber > 0) && !flag) {
+ if (_articleNumber == -1) {
+ if (_v86F3A >= 0) {
+ int articleNum = 0;
+
+ if ((_v86F42 == 2) || (_v86F42 == 5)) {
+ int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_hotspotId);
+ articleNum = _madsVm->globals()->getObject(objectId)->article;
+ } else if (_v86F3A < hotspotCount) {
+ articleNum = (*_madsVm->scene()->getSceneResources().hotspots)[_hotspotId].getArticle();
+ } else {
+
+ }
+ }
+
+ } else if ((_articleNumber == kVerbLook) || (_vm->getGameType() != GType_RexNebular) ||
+ (strcmp(_madsVm->globals()->getVocab(_action.indirectObjectId), fenceStr) != 0)) {
+ // Write out the article
+ strcat(_statusText, englishMADSArticleList[_articleNumber]);
+ } else {
+ // Special case for a 'fence' entry in Rex Nebular
+ strcat(_statusText, overStr);
+ }
+
+ strcat(_statusText, " ");
+ }
+
+ // Append object description if necessary
+ if (_v86F3A >= 0)
+ appendVocab(_action.indirectObjectId);
+
+ // Remove any trailing space character
+ int statusLen = strlen(_statusText);
+ if ((statusLen > 0) && (_statusText[statusLen - 1] == ' '))
+ _statusText[statusLen - 1] = '\0';
+ }
+
+ _textChanged = true;
+}
+
+void MadsAction::refresh() {
+ // Exit immediately if nothing has changed
+ if (!_textChanged)
+ return;
+
+ // Remove any old copy of the status text
+ if (_statusTextIndex >= 0) {
+ _owner._textDisplay.expire(_statusTextIndex);
+ _statusTextIndex = -1;
+ }
+
+ if (strlen(_statusText) != 0) {
+ if ((_owner._screenObjects._v832EC == 0) || (_owner._screenObjects._v832EC == 2)) {
+ Font *font = _madsVm->_font->getFont(FONT_MAIN_MADS);
+ int textSpacing = -1;
+
+ int strWidth = font->getWidth(_statusText);
+ if (strWidth > 320) {
+ // Too large to fit, so fall back on interface font
+ font = _madsVm->_font->getFont(FONT_INTERFACE_MADS);
+ strWidth = font->getWidth(_statusText, 0);
+ textSpacing = 0;
+ }
+
+ // Add a new text display entry to display the status text at the bottom of the screen area
+ uint colours = (_vm->getGameType() == GType_DragonSphere) ? 0x0300 : 0x0003;
+
+ _statusTextIndex = _owner._textDisplay.add(160 - (strWidth / 2),
+ MADS_SURFACE_HEIGHT + _owner._posAdjust.y - 13, colours, textSpacing, _statusText, font);
+ }
+ }
+
+ _textChanged = false;
+}
+
+void MadsAction::startAction() {
+ _madsVm->_player.moveComplete();
+
+ _inProgress = true;
+ _v8453A = 0;
+ _savedFields.selectedRow = _selectedRow;
+ _savedFields.articleNumber = _articleNumber;
+ _savedFields.actionMode = _actionMode;
+ _savedFields.actionMode2 = _actionMode2;
+ _savedFields.lookFlag = _lookFlag;
+ int savedHotspotId = _hotspotId;
+ int savedV86F3A = _v86F3A;
+ int savedV86F42 = _v86F42;
+
+ // Copy the action to be active
+ _activeAction = _action;
+ strcpy(_dialogTitle, _statusText);
+
+ if ((_savedFields.actionMode2 == ACTMODE2_4) && (savedV86F42 == 0))
+ _v8453A = true;
+
+ _startWalkFlag = false;
+ int hotspotId = -1;
+ HotSpotList &dynHotspots = *_madsVm->scene()->getSceneResources().dynamicHotspots;
+ HotSpotList &hotspots = *_madsVm->scene()->getSceneResources().hotspots;
+
+ if (!_savedFields.lookFlag && (_madsVm->scene()->_screenObjects._v832EC != 1)) {
+ if (_savedFields.actionMode2 == ACTMODE2_4)
+ hotspotId = savedHotspotId;
+ else if (savedV86F42 == 4)
+ hotspotId = savedV86F3A;
+
+ if (hotspotId >= hotspots.size()) {
+ HotSpot &hs = dynHotspots[hotspotId - hotspots.size()];
+ if ((hs.getFeetX() == -1) || (hs.getFeetX() == -3)) {
+ if (_v86F4A && ((hs.getFeetX() == -3) || (_savedFields.selectedRow < 0))) {
+ _startWalkFlag = true;
+ _madsVm->scene()->_destPos = _madsVm->scene()->_customDest;
+ }
+ } else if ((hs.getFeetX() >= 0) && ((_savedFields.actionMode == ACTMODE_NONE) || (hs.getCursor() < 2))) {
+ _startWalkFlag = true;
+ _madsVm->scene()->_destPos.x = hs.getFeetX();
+ _madsVm->scene()->_destPos.y = hs.getFeetY();
+ }
+ _madsVm->scene()->_destFacing = hs.getFacing();
+ hotspotId = -1;
+ }
+ }
+
+ if (hotspotId >= 0) {
+ HotSpot &hs = hotspots[hotspotId];
+ if ((hs.getFeetX() == -1) || (hs.getFeetX() == -3)) {
+ if (_v86F4A && ((hs.getFeetX() == -3) || (_savedFields.selectedRow < 0))) {
+ _startWalkFlag = true;
+ _madsVm->scene()->_destPos = _madsVm->scene()->_customDest;
+ }
+ } else if ((hs.getFeetX() >= 0) && ((_savedFields.actionMode == ACTMODE_NONE) || (hs.getCursor() < 2))) {
+ _startWalkFlag = true;
+ _madsVm->scene()->_destPos.x = hs.getFeetX();
+ _madsVm->scene()->_destPos.y = hs.getFeetY();
+ }
+ _madsVm->scene()->_destFacing = hs.getFacing();
+ }
+
+ _walkFlag = _startWalkFlag;
+}
+
+void MadsAction::checkAction() {
+ if (isAction(kVerbLookAt) || isAction(kVerbThrow))
+ _startWalkFlag = 0;
+}
+
+bool MadsAction::isAction(int verbId, int objectNameId, int indirectObjectId) {
+ if (_activeAction.verbId != verbId)
+ return false;
+ if ((objectNameId != 0) && (_activeAction.objectNameId != objectNameId))
+ return false;
+ if ((indirectObjectId != 0) && (_activeAction.indirectObjectId != indirectObjectId))
+ return false;
+ return true;
+}
+
+//--------------------------------------------------------------------------
+
+bool MadsSpriteSlot::operator==(const SpriteSlotSubset &other) const {
+ return (spriteListIndex == other.spriteListIndex) && (frameNumber == other.frameNumber) &&
+ (xp == other.xp) && (yp == other.yp) && (depth == other.depth) && (scale == other.scale);
+}
+
+void MadsSpriteSlot::copy(const SpriteSlotSubset &other) {
+ spriteListIndex = other.spriteListIndex;
+ frameNumber = other.frameNumber;
+ xp = other.xp;
+ yp = other.yp;
+ depth = other.depth;
+ scale = other.scale;
+}
//--------------------------------------------------------------------------
-MadsSpriteSlots::MadsSpriteSlots() {
+MadsSpriteSlots::MadsSpriteSlots(MadsView &owner): _owner(owner) {
for (int i = 0; i < SPRITE_SLOTS_SIZE; ++i) {
MadsSpriteSlot rec;
_entries.push_back(rec);
@@ -52,6 +363,23 @@ MadsSpriteSlots::MadsSpriteSlots() {
startIndex = 0;
}
+MadsSpriteSlots::~MadsSpriteSlots() {
+ for (uint i = 0; i < _sprites.size(); ++i)
+ delete _sprites[i];
+}
+
+void MadsSpriteSlots::clear() {
+ _owner._textDisplay.clear();
+ for (uint i = 0; i < _sprites.size(); ++i)
+ delete _sprites[i];
+ _sprites.clear();
+
+ // Reset the sprite slots list back to a single entry for a full screen refresh
+ startIndex = 1;
+ _entries[0].spriteType = FULL_SCREEN_REFRESH;
+ _entries[0].seqIndex = -1;
+}
+
int MadsSpriteSlots::getIndex() {
if (startIndex == SPRITE_SLOTS_SIZE)
error("Run out of sprite slots");
@@ -59,25 +387,48 @@ int MadsSpriteSlots::getIndex() {
return startIndex++;
}
-int MadsSpriteSlots::addSprites(const char *resName) {
+int MadsSpriteSlots::addSprites(const char *resName, bool suppressErrors, int flags) {
+ // If errors are suppressed, first check if the resource exists
+ if (suppressErrors) {
+ if (!_vm->res()->resourceExists(resName))
+ return -1;
+ }
+
// Get the sprite set
Common::SeekableReadStream *data = _vm->res()->get(resName);
- SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName);
+ SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName, false, flags);
spriteSet->translate(_madsVm->_palette);
+ assert(spriteSet != NULL);
- _sprites.push_back(SpriteList::value_type(spriteSet));
+ _sprites.push_back(spriteSet);
_vm->res()->toss(resName);
return _sprites.size() - 1;
}
+int MadsSpriteSlots::addSprites(SpriteAsset *spriteSet) {
+ _sprites.push_back(spriteSet);
+
+ return _sprites.size() - 1;
+}
+
+void MadsSpriteSlots::deleteSprites(int listIndex) {
+ if (listIndex < 0)
+ return;
+
+ delete _sprites[listIndex];
+ _sprites[listIndex] = NULL;
+ if (listIndex == ((int)_sprites.size() - 1))
+ _sprites.remove_at(listIndex);
+}
+
/*
* Deletes the sprite slot with the given timer entry
*/
-void MadsSpriteSlots::deleteTimer(int timerIndex) {
+void MadsSpriteSlots::deleteTimer(int seqIndex) {
for (int idx = 0; idx < startIndex; ++idx) {
- if (_entries[idx].timerIndex == timerIndex)
- _entries[idx].spriteId = -1;
+ if (_entries[idx].seqIndex == seqIndex)
+ _entries[idx].spriteType = EXPIRED_SPRITE;
}
}
@@ -95,13 +446,53 @@ bool sortHelper(const DepthEntry &entry1, const DepthEntry &entry2) {
typedef Common::List<DepthEntry> DepthList;
-void MadsSpriteSlots::draw(View *view) {
+void MadsSpriteSlots::drawBackground() {
+ // Draw all active sprites onto the background surface
+ for (int i = 0; i < startIndex; ++i) {
+ MadsSpriteSlot &slot = _entries[i];
+
+ if (slot.spriteType >= 0) {
+ _owner._dirtyAreas[i].active = false;
+ } else {
+ _owner._dirtyAreas[i].textActive = true;
+ _owner._dirtyAreas.setSpriteSlot(i, slot);
+
+ if (slot.spriteType == BACKGROUND_SPRITE) {
+ SpriteAsset &spriteSet = getSprite(slot.spriteListIndex);
+ M4Sprite *frame = spriteSet.getFrame((slot.frameNumber & 0x7fff) - 1);
+ int xp = slot.xp;
+ int yp = slot.yp;
+
+ if (_entries[i].scale != -1) {
+ // Adjust position based on frame size
+ xp -= frame->width() / 2;
+ yp -= frame->height() / 2;
+ }
+
+ if (slot.depth > 1) {
+ // Draw the frame with depth processing
+ _owner._bgSurface->copyFrom(frame, xp, yp, slot.depth, _owner._depthSurface, 100,
+ frame->getTransparencyIndex());
+ } else {
+ // No depth, so simply draw the image
+ frame->copyTo(_owner._bgSurface, xp, yp, frame->getTransparencyIndex());
+ }
+ }
+ }
+ }
+
+ // Flag any remaining dirty areas as inactive
+ for (uint i = startIndex; i < DIRTY_AREAS_TEXT_DISPLAY_IDX; ++i)
+ _owner._dirtyAreas[i].active = false;
+}
+
+void MadsSpriteSlots::drawForeground(M4Surface *viewport) {
DepthList depthList;
// Get a list of sprite object depths for active objects
for (int i = 0; i < startIndex; ++i) {
- if (_entries[i].spriteId >= 0) {
- DepthEntry rec(_entries[i].depth, i);
+ if (_entries[i].spriteType >= SPRITE_ZERO) {
+ DepthEntry rec(16 - _entries[i].depth, i);
depthList.push_back(rec);
}
}
@@ -115,42 +506,78 @@ void MadsSpriteSlots::draw(View *view) {
DepthEntry &de = *i;
MadsSpriteSlot &slot = _entries[de.index];
assert(slot.spriteListIndex < (int)_sprites.size());
- SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex].get();
+ SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex];
+
+ // Get the sprite frame
+ int frameNumber = slot.frameNumber & 0x7fff;
+ bool flipped = (slot.frameNumber & 0x8000) != 0;
+ M4Sprite *sprite = spriteSet.getFrame(frameNumber - 1);
- if (slot.scale < 100) {
+ M4Surface *spr = sprite;
+ if (flipped) {
+ // Create a flipped copy of the sprite temporarily
+ spr = sprite->flipHorizontal();
+ }
+
+ if ((slot.scale < 100) && (slot.scale != -1)) {
// Minimalised drawing
- assert(slot.spriteListIndex < (int)_sprites.size());
- M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1);
- spr->draw1(view, slot.scale, slot.depth, slot.xp, slot.yp);
+ viewport->copyFrom(spr, slot.xp, slot.yp, slot.depth, _owner._depthSurface, slot.scale,
+ sprite->getTransparencyIndex());
} else {
int xp, yp;
- M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1);
if (slot.scale == -1) {
- xp = slot.xp; // - widthAdjust;
- yp = slot.yp; // - heightAdjust;
+ xp = slot.xp - _owner._posAdjust.x;
+ yp = slot.yp - _owner._posAdjust.y;
} else {
- xp = slot.xp - (spr->width() / 2); // - widthAdjust;
- yp = slot.yp - spr->height() + 1; // - heightAdjust;
+ xp = slot.xp - (spr->width() / 2) - _owner._posAdjust.x;
+ yp = slot.yp - spr->height() - _owner._posAdjust.y + 1;
}
if (slot.depth > 1) {
- spr->draw2(view, slot.depth, xp, yp);
+ // Draw the frame with depth processing
+ viewport->copyFrom(spr, xp, yp, slot.depth, _owner._depthSurface, 100, sprite->getTransparencyIndex());
} else {
- spr->draw3(view, xp, yp);
+ // No depth, so simply draw the image
+ spr->copyTo(viewport, xp, yp, sprite->getTransparencyIndex());
}
}
+
+ // Free sprite if it was a flipped one
+ if (flipped)
+ delete spr;
+ }
+}
+
+void MadsSpriteSlots::setDirtyAreas() {
+ for (int i = 0; i < startIndex; ++i) {
+ if (_entries[i].spriteType >= 0) {
+ _owner._dirtyAreas.setSpriteSlot(i, _entries[i]);
+
+ _owner._dirtyAreas[i].textActive = (_entries[i].spriteType <= 0) ? 0 : 1;
+ _entries[i].spriteType = 0;
+ }
}
}
/**
+ * Flags the entire screen to be redrawn during the next drawing cycle
+ */
+void MadsSpriteSlots::fullRefresh() {
+ int idx = getIndex();
+
+ _entries[idx].spriteType = FULL_SCREEN_REFRESH;
+ _entries[idx].seqIndex = -1;
+}
+
+/**
* Removes any sprite slots that are no longer needed
*/
void MadsSpriteSlots::cleanUp() {
// Delete any entries that aren't needed
int idx = 0;
while (idx < startIndex) {
- if (_entries[idx].spriteId >= 0) {
+ if (_entries[idx].spriteType < 0) {
_entries.remove_at(idx);
--startIndex;
} else {
@@ -168,10 +595,11 @@ void MadsSpriteSlots::cleanUp() {
//--------------------------------------------------------------------------
-MadsTextDisplay::MadsTextDisplay() {
+MadsTextDisplay::MadsTextDisplay(MadsView &owner): _owner(owner) {
for (int i = 0; i < TEXT_DISPLAY_SIZE; ++i) {
MadsTextDisplayEntry rec;
rec.active = false;
+ rec.expire = 0;
_entries.push_back(rec);
}
}
@@ -206,22 +634,35 @@ int MadsTextDisplay::add(int xp, int yp, uint fontColour, int charSpacing, const
return usedSlot;
}
-void MadsTextDisplay::draw(View *view) {
- for (uint idx = 0; idx < _entries.size(); ++idx) {
+void MadsTextDisplay::setDirtyAreas() {
+ // Determine dirty areas for active text areas
+ for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) {
+ if ((_entries[idx].expire >= 0) || !_entries[idx].active)
+ _owner._dirtyAreas[dirtyIdx].active = false;
+ else {
+ _owner._dirtyAreas[dirtyIdx].textActive = true;
+ _owner._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]);
+ }
+ }
+}
+
+void MadsTextDisplay::setDirtyAreas2() {
+ // Determine dirty areas for active text areas
+ for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) {
if (_entries[idx].active && (_entries[idx].expire >= 0)) {
- _entries[idx].font->setColours(_entries[idx].colour1,
- (_entries[idx].colour2 == 0) ? _entries[idx].colour1 : _entries[idx].colour2, 0xff);
- _entries[idx].font->writeString(view, _entries[idx].msg,
- _entries[idx].bounds.left, _entries[idx].bounds.top, _entries[idx].bounds.width(),
- _entries[idx].spacing);
+ _owner._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]);
+ _owner._dirtyAreas[dirtyIdx].textActive = (_entries[idx].expire <= 0) ? 0 : 1;
}
}
+}
- // Clear up any now text display entries that are to be expired
+void MadsTextDisplay::draw(M4Surface *view) {
for (uint idx = 0; idx < _entries.size(); ++idx) {
- if (_entries[idx].expire < 0) {
- _entries[idx].active = false;
- _entries[idx].expire = 0;
+ if (_entries[idx].active && (_entries[idx].expire >= 0)) {
+ _entries[idx].font->setColours(_entries[idx].colour1, _entries[idx].colour2, 0);
+ _entries[idx].font->writeString(view, _entries[idx].msg,
+ _entries[idx].bounds.left, _entries[idx].bounds.top, _entries[idx].bounds.width(),
+ _entries[idx].spacing);
}
}
}
@@ -242,12 +683,13 @@ void MadsTextDisplay::cleanUp() {
MadsKernelMessageList::MadsKernelMessageList(MadsView &owner): _owner(owner) {
for (int i = 0; i < TIMED_TEXT_SIZE; ++i) {
- MadsKernelMessageListEntry rec;
+ MadsKernelMessageEntry rec;
_entries.push_back(rec);
}
_owner._textSpacing = -1;
_talkFont = _vm->_font->getFont(FONT_CONVERSATION_MADS);
+ word_8469E = 0;
}
void MadsKernelMessageList::clear() {
@@ -258,20 +700,20 @@ void MadsKernelMessageList::clear() {
_talkFont = _vm->_font->getFont(FONT_CONVERSATION_MADS);
}
-int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg) {
+int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg) {
// Find a free slot
uint idx = 0;
while ((idx < _entries.size()) && ((_entries[idx].flags & KMSG_ACTIVE) != 0))
++idx;
if (idx == _entries.size()) {
- if (v2 == 0)
+ if (abortTimers == 0)
return -1;
error("MadsKernelList overflow");
}
- MadsKernelMessageListEntry &rec = _entries[idx];
- rec.msg = msg;
+ MadsKernelMessageEntry &rec = _entries[idx];
+ strcpy(rec.msg, msg);
rec.flags = flags | KMSG_ACTIVE;
rec.colour1 = fontColour & 0xff;
rec.colour2 = fontColour >> 8;
@@ -279,56 +721,56 @@ int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 f
rec.textDisplayIndex = -1;
rec.timeout = timeout;
rec.frameTimer = _madsVm->_currentTimer;
- rec.field_1C = v2;
+ rec.abortTimers = abortTimers;
rec.abortMode = _owner._abortTimersMode2;
for (int i = 0; i < 3; ++i)
- rec.actionNouns[i] = _madsVm->scene()->actionNouns[i];
+ rec.actionNouns[i] = _madsVm->globals()->actionNouns[i];
- if (flags & KMSG_2)
- rec.frameTimer = _owner._ticksAmount + _owner._newTimeout;
+ if (flags & KMSG_PLAYER_TIMEOUT)
+ rec.frameTimer = _madsVm->_player._ticksAmount + _madsVm->_player._priorTimer;
return idx;
}
-int MadsKernelMessageList::addQuote(int quoteId, int v2, uint32 timeout) {
+int MadsKernelMessageList::addQuote(int quoteId, int abortTimers, uint32 timeout) {
const char *quoteStr = _madsVm->globals()->getQuote(quoteId);
- return add(Common::Point(0, 0), 0x1110, KMSG_2 | KMSG_20, v2, timeout, quoteStr);
+ return add(Common::Point(0, 0), 0x1110, KMSG_PLAYER_TIMEOUT | KMSG_CENTER_ALIGN, abortTimers, timeout, quoteStr);
}
-void MadsKernelMessageList::unk1(int msgIndex, int v1, int v2) {
+void MadsKernelMessageList::scrollMessage(int msgIndex, int numTicks, bool quoted) {
if (msgIndex < 0)
return;
- _entries[msgIndex].flags |= (v2 == 0) ? KMSG_8 : (KMSG_8 | KMSG_1);
+ _entries[msgIndex].flags |= quoted ? (KMSG_SCROLL | KMSG_QUOTED) : KMSG_SCROLL;
_entries[msgIndex].msgOffset = 0;
- _entries[msgIndex].field_E = v1;
+ _entries[msgIndex].numTicks = numTicks;
_entries[msgIndex].frameTimer2 = _madsVm->_currentTimer;
const char *msgP = _entries[msgIndex].msg;
_entries[msgIndex].asciiChar = *msgP;
_entries[msgIndex].asciiChar2 = *(msgP + 1);
- if (_entries[msgIndex].flags & KMSG_2)
- _entries[msgIndex].frameTimer2 = _owner._ticksAmount + _owner._newTimeout;
+ if (_entries[msgIndex].flags & KMSG_PLAYER_TIMEOUT)
+ _entries[msgIndex].frameTimer2 = _madsVm->_player._ticksAmount + _madsVm->_player._priorTimer;
_entries[msgIndex].frameTimer = _entries[msgIndex].frameTimer2;
}
void MadsKernelMessageList::setSeqIndex(int msgIndex, int seqIndex) {
if (msgIndex >= 0) {
- _entries[msgIndex].flags |= KMSG_4;
+ _entries[msgIndex].flags |= KMSG_SEQ_ENTRY;
_entries[msgIndex].sequenceIndex = seqIndex;
}
}
void MadsKernelMessageList::remove(int msgIndex) {
- MadsKernelMessageListEntry &rec = _entries[msgIndex];
+ MadsKernelMessageEntry &rec = _entries[msgIndex];
if (rec.flags & KMSG_ACTIVE) {
- if (rec.flags & KMSG_8) {
- //*(rec.msg + rec.msgOffset) = rec.asciiChar;
- //*(rec.msg + rec.msgOffset + 1) = rec.asciiChar2;
+ if (rec.flags & KMSG_SCROLL) {
+ *(rec.msg + rec.msgOffset) = rec.asciiChar;
+ *(rec.msg + rec.msgOffset + 1) = rec.asciiChar2;
}
if (rec.textDisplayIndex >= 0)
@@ -345,8 +787,150 @@ void MadsKernelMessageList::reset() {
// sub_20454
}
+void MadsKernelMessageList::update() {
+ uint32 currentTimer = _madsVm->_currentTimer;
+
+ for (uint i = 0; i < _entries.size(); ++i) {
+ if (((_entries[i].flags & KMSG_ACTIVE) != 0) && (currentTimer >= _entries[i].frameTimer))
+ processText(i);
+ }
+}
+
+void MadsKernelMessageList::processText(int msgIndex) {
+ MadsKernelMessageEntry &msg = _entries[msgIndex];
+ uint32 currentTimer = _madsVm->_currentTimer;
+ bool flag = false;
+
+ if ((msg.flags & KMSG_EXPIRE) != 0) {
+ _owner._textDisplay.expire(msg.textDisplayIndex);
+ msg.flags &= !KMSG_ACTIVE;
+ return;
+ }
+
+ if ((msg.flags & KMSG_SCROLL) == 0) {
+ msg.timeout -= 3;
+ }
+
+ if (msg.flags & KMSG_SEQ_ENTRY) {
+ MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex];
+ if (seqEntry.doneFlag || !seqEntry.active)
+ msg.timeout = 0;
+ }
+
+ if ((msg.timeout <= 0) && (_owner._abortTimers == 0)) {
+ msg.flags |= KMSG_EXPIRE;
+ if (msg.abortTimers != 0) {
+ _owner._abortTimers = msg.abortTimers;
+ _owner._abortTimersMode = msg.abortMode;
+
+ if (_owner._abortTimersMode != ABORTMODE_1) {
+ for (int i = 0; i < 3; ++i)
+ _madsVm->globals()->actionNouns[i] = msg.actionNouns[i];
+ }
+ }
+ }
+
+ msg.frameTimer = currentTimer + 3;
+ int x1 = 0, y1 = 0;
+
+ if (msg.flags & KMSG_SEQ_ENTRY) {
+ MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex];
+ if (!seqEntry.nonFixed) {
+ SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex);
+ M4Sprite *frame = spriteSet.getFrame(seqEntry.frameIndex - 1);
+ x1 = frame->bounds().left;
+ y1 = frame->bounds().top;
+ } else {
+ x1 = seqEntry.msgPos.x;
+ y1 = seqEntry.msgPos.y;
+ }
+ }
+
+ if (msg.flags & KMSG_PLAYER_TIMEOUT) {
+ if (word_8469E != 0) {
+ // TODO: Figure out various flags
+ } else {
+ x1 = 160;
+ y1 = 78;
+ }
+ }
+
+ x1 += msg.position.x;
+ y1 += msg.position.y;
+
+ if ((msg.flags & KMSG_SCROLL) && (msg.frameTimer >= currentTimer)) {
+ msg.msg[msg.msgOffset] = msg.asciiChar;
+ char *msgP = &msg.msg[++msg.msgOffset];
+ *msgP = msg.asciiChar2;
+
+ msg.asciiChar = *msgP;
+ msg.asciiChar2 = *(msgP + 1);
+
+ if (!msg.asciiChar) {
+ // End of message
+ *msgP = '\0';
+ msg.flags &= ~KMSG_SCROLL;
+ } else if (msg.flags & KMSG_QUOTED) {
+ *msgP = '"';
+ *(msgP + 1) = '\0';
+ }
+
+ msg.frameTimer = msg.frameTimer2 = currentTimer + msg.numTicks;
+ flag = true;
+ }
+
+ int strWidth = _talkFont->getWidth(msg.msg, _owner._textSpacing);
+
+ if (msg.flags & (KMSG_RIGHT_ALIGN | KMSG_CENTER_ALIGN)) {
+ x1 -= (msg.flags & KMSG_CENTER_ALIGN) ? strWidth / 2 : strWidth;
+ }
+
+ // Make sure text appears entirely on-screen
+ int x2 = x1 + strWidth;
+ if (x2 > MADS_SURFACE_WIDTH)
+ x1 -= x2 - MADS_SURFACE_WIDTH;
+ if (x1 > (MADS_SURFACE_WIDTH - 1))
+ x1 = MADS_SURFACE_WIDTH - 1;
+ if (x1 < 0)
+ x1 = 0;
+
+ if (y1 > (MADS_SURFACE_HEIGHT - 1))
+ y1 = MADS_SURFACE_HEIGHT - 1;
+ if (y1 < 0)
+ y1 = 0;
+
+ if (msg.textDisplayIndex >= 0) {
+ MadsTextDisplayEntry textEntry = _owner._textDisplay[msg.textDisplayIndex];
+
+ if (flag || (textEntry.bounds.left != x1) || (textEntry.bounds.top != y1)) {
+ // Mark the associated text entry as deleted, so it can be re-created
+ _owner._textDisplay.expire(msg.textDisplayIndex);
+ msg.textDisplayIndex = -1;
+ }
+ }
+
+ if (msg.textDisplayIndex < 0) {
+ // Need to create a new text display entry for this message
+ int idx = _owner._textDisplay.add(x1, y1, msg.colour1 | (msg.colour2 << 8), _owner._textSpacing, msg.msg, _talkFont);
+ if (idx >= 0)
+ msg.textDisplayIndex = idx;
+ }
+}
+
//--------------------------------------------------------------------------
+ScreenObjects::ScreenObjects(MadsView &owner): _owner(owner) {
+ _v832EC = 0;
+ _v7FECA = 0;
+ _v7FED6 = 0;
+ _v8332A = 0;
+ _yp = 0;
+ _v8333C = 0;
+ _selectedObject = 0;
+ _category = 0;
+ _objectIndex = 0;
+}
+
/**
* Clears the entries list
*/
@@ -399,27 +983,52 @@ void ScreenObjects::setActive(int category, int idx, bool active) {
}
}
+void ScreenObjects::check(bool scanFlag, bool mouseClick) {
+ if (!mouseClick || _v832EC)
+ _v7FECA = 0;
+
+ if (!_v7FED6 && !_v8332A && !_yp && (_v8333C != 0)) {
+ if (scanFlag) {
+ _category = CAT_NONE;
+ _selectedObject = scanBackwards(_madsVm->_mouse->currentPos().x, _madsVm->_mouse->currentPos().y,
+ LAYER_GUI);
+
+ if (_selectedObject > 0) {
+ ScreenObjectEntry &obj = _entries[_selectedObject];
+ _category = obj.category & 7;
+ _objectIndex = obj.index;
+ }
+
+ // TODO: Other stuff related to the user interface
+ }
+ }
+
+ _owner._action.refresh();
+}
+
/*--------------------------------------------------------------------------*/
MadsDynamicHotspots::MadsDynamicHotspots(MadsView &owner): _owner(owner) {
for (int i = 0; i < DYNAMIC_HOTSPOTS_SIZE; ++i) {
DynamicHotspot rec;
rec.active = false;
+ _entries.push_back(rec);
}
- _flag = true;
+ _changed = true;
_count = 0;
}
-int MadsDynamicHotspots::add(int descId, int field14, int timerIndex, const Common::Rect &bounds) {
+int MadsDynamicHotspots::add(int descId, int field14, int seqIndex, const Common::Rect &bounds) {
// Find a free slot
uint idx = 0;
- while ((idx < _entries.size()) && !_entries[idx].active)
+ while ((idx < _entries.size()) && _entries[idx].active)
++idx;
if (idx == _entries.size())
error("MadsDynamicHotspots overflow");
_entries[idx].active = true;
_entries[idx].descId = descId;
+ _entries[idx].seqIndex = seqIndex;
_entries[idx].bounds = bounds;
_entries[idx].pos.x = -3;
_entries[idx].pos.y = 0;
@@ -429,8 +1038,10 @@ int MadsDynamicHotspots::add(int descId, int field14, int timerIndex, const Comm
_entries[idx].field_17 = 0;
++_count;
- _flag = true;
- _owner._sequenceList[timerIndex].dynamicHotspotIndex = idx;
+ _changed = true;
+
+ if (seqIndex >= 0)
+ _owner._sequenceList[seqIndex].dynamicHotspotIndex = idx;
return idx;
}
@@ -454,12 +1065,12 @@ int MadsDynamicHotspots::set17(int index, int v) {
void MadsDynamicHotspots::remove(int index) {
if (_entries[index].active) {
- if (_entries[index].timerIndex >= 0)
- _owner._sequenceList[_entries[index].timerIndex].dynamicHotspotIndex = -1;
+ if (_entries[index].seqIndex >= 0)
+ _owner._sequenceList[_entries[index].seqIndex].dynamicHotspotIndex = -1;
_entries[index].active = false;
--_count;
- _flag = true;
+ _changed = true;
}
}
@@ -468,7 +1079,160 @@ void MadsDynamicHotspots::reset() {
_entries[i].active = false;
_count = 0;
- _flag = false;
+ _changed = false;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void MadsDirtyArea::setArea(int width, int height, int maxWidth, int maxHeight) {
+ if (bounds.left % 2) {
+ --bounds.left;
+ ++width;
+ }
+
+ if (bounds.left < 0)
+ bounds.left = 0;
+ else if (bounds.left > maxWidth)
+ bounds.left = maxWidth;
+ int right = bounds.left + width;
+ if (right < 0)
+ right = 0;
+ if (right > maxWidth)
+ right = maxWidth;
+
+ bounds.right = right;
+ bounds2.left = bounds.width() / 2;
+ bounds2.right = bounds.left + (bounds.width() + 1) / 2 - 1;
+
+ if (bounds.top < 0)
+ bounds.top = 0;
+ else if (bounds.top > maxHeight)
+ bounds.top = maxHeight;
+ int bottom = bounds.top + height;
+ if (bottom < 0)
+ bottom = 0;
+ if (bottom > maxHeight)
+ bottom = maxHeight;
+
+ bounds.bottom = bottom;
+ bounds2.top = bounds.height() / 2;
+ bounds2.bottom = bounds.top + (bounds.height() + 1) / 2 - 1;
+
+ active = true;
+}
+
+/*--------------------------------------------------------------------------*/
+
+MadsDirtyAreas::MadsDirtyAreas(MadsView &owner): _owner(owner) {
+ for (int i = 0; i < DIRTY_AREAS_SIZE; ++i) {
+ MadsDirtyArea rec;
+ rec.active = false;
+ _entries.push_back(rec);
+ }
+}
+
+void MadsDirtyAreas::setSpriteSlot(int dirtyIdx, const MadsSpriteSlot &spriteSlot) {
+ int width, height;
+ MadsDirtyArea &dirtyArea = _entries[dirtyIdx];
+
+ if (spriteSlot.spriteType == FULL_SCREEN_REFRESH) {
+ // Special entry to refresh the entire screen
+ dirtyArea.bounds.left = 0;
+ dirtyArea.bounds.top = 0;
+ width = MADS_SURFACE_WIDTH;
+ height = MADS_SURFACE_HEIGHT;
+ } else {
+ // Standard sprite slots
+ dirtyArea.bounds.left = spriteSlot.xp - _owner._posAdjust.x;
+ dirtyArea.bounds.top = spriteSlot.yp - _owner._posAdjust.y;
+
+ SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(spriteSlot.spriteListIndex);
+ M4Sprite *frame = spriteSet.getFrame(((spriteSlot.frameNumber & 0x7fff) - 1) & 0x7f);
+
+ if (spriteSlot.scale == -1) {
+ width = frame->width();
+ height = frame->height();
+ } else {
+ width = frame->width() * spriteSlot.scale / 100;
+ height = frame->height() * spriteSlot.scale / 100;
+
+ dirtyArea.bounds.left -= width / 2;
+ dirtyArea.bounds.top += -(height - 1);
+ }
+ }
+
+ dirtyArea.setArea(width, height, MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT);
+}
+
+void MadsDirtyAreas::setTextDisplay(int dirtyIdx, const MadsTextDisplayEntry &textDisplay) {
+ MadsDirtyArea &dirtyArea = _entries[dirtyIdx];
+ dirtyArea.bounds.left = textDisplay.bounds.left;
+ dirtyArea.bounds.top = textDisplay.bounds.top;
+
+ dirtyArea.setArea(textDisplay.bounds.width(), textDisplay.bounds.height(), MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT);
+}
+
+/**
+ * Merge together any designated dirty areas that overlap
+ * @param startIndex 1-based starting dirty area starting index
+ * @param count Number of entries to process
+ */
+void MadsDirtyAreas::merge(int startIndex, int count) {
+return;//***DEBUG***
+ if (startIndex >= count)
+ return;
+
+ for (int outerCtr = startIndex - 1, idx = 0; idx < count; ++outerCtr, ++idx) {
+ if (!_entries[outerCtr].active)
+ continue;
+
+ for (int innerCtr = outerCtr + 1; innerCtr < count; ++innerCtr) {
+ if (!_entries[innerCtr].active || !intersects(outerCtr, innerCtr))
+ continue;
+
+ if (_entries[outerCtr].textActive && _entries[innerCtr].textActive)
+ mergeAreas(outerCtr, innerCtr);
+ }
+ }
+}
+
+/**
+ * Returns true if two dirty areas intersect
+ */
+bool MadsDirtyAreas::intersects(int idx1, int idx2) {
+ return _entries[idx1].bounds2.intersects(_entries[idx2].bounds2);
+}
+
+void MadsDirtyAreas::mergeAreas(int idx1, int idx2) {
+ MadsDirtyArea &da1 = _entries[idx1];
+ MadsDirtyArea &da2 = _entries[idx2];
+
+ da1.bounds.extend(da2.bounds);
+
+ da1.bounds2.left = da1.bounds.width() / 2;
+ da1.bounds2.right = da1.bounds.left + (da1.bounds.width() + 1) / 2 - 1;
+ da1.bounds2.top = da1.bounds.height() / 2;
+ da1.bounds2.bottom = da1.bounds.top + (da1.bounds.height() + 1) / 2 - 1;
+
+ da2.active = false;
+ da1.textActive = true;
+}
+
+void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src, const Common::Point &posAdjust) {
+ for (uint i = 0; i < _entries.size(); ++i) {
+ const Common::Rect &srcBounds = _entries[i].bounds;
+
+ Common::Rect bounds(srcBounds.left + posAdjust.x, srcBounds.top + posAdjust.y,
+ srcBounds.right + posAdjust.x, srcBounds.bottom + posAdjust.y);
+
+ if (_entries[i].active && _entries[i].bounds.isValidRect())
+ src->copyTo(dest, bounds, _entries[i].bounds.left, _entries[i].bounds.top);
+ }
+}
+
+void MadsDirtyAreas::clear() {
+ for (uint i = 0; i < _entries.size(); ++i)
+ _entries[i].active = false;
}
/*--------------------------------------------------------------------------*/
@@ -501,15 +1265,15 @@ bool MadsSequenceList::addSubEntry(int index, SequenceSubEntryMode mode, int fra
return false;
}
-int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks,
- int height, int width, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites,
+int MadsSequenceList::add(int spriteListIndex, bool flipped, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks,
+ int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites,
int frameStart) {
// Find a free slot
- uint timerIndex = 0;
- while ((timerIndex < _entries.size()) && (_entries[timerIndex].active))
- ++timerIndex;
- if (timerIndex == _entries.size())
+ uint seqIndex = 0;
+ while ((seqIndex < _entries.size()) && (_entries[seqIndex].active))
+ ++seqIndex;
+ if (seqIndex == _entries.size())
error("TimerList full");
if (frameStart <= 0)
@@ -520,77 +1284,76 @@ int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int trigg
frameInc = 0;
// Set the list entry fields
- _entries[timerIndex].active = true;
- _entries[timerIndex].spriteListIndex = spriteListIndex;
- _entries[timerIndex].field_2 = v0;
- _entries[timerIndex].frameIndex = frameIndex;
- _entries[timerIndex].frameStart = frameStart;
- _entries[timerIndex].numSprites = numSprites;
- _entries[timerIndex].animType = animType;
- _entries[timerIndex].frameInc = frameInc;
- _entries[timerIndex].depth = depth;
- _entries[timerIndex].scale = scale;
- _entries[timerIndex].field_12 = field_12;
- _entries[timerIndex].width = width;
- _entries[timerIndex].height = height;
- _entries[timerIndex].numTicks = numTicks;
- _entries[timerIndex].extraTicks = extraTicks;
-
- _entries[timerIndex].timeout = _madsVm->_currentTimer + delayTicks;
-
- _entries[timerIndex].triggerCountdown = triggerCountdown;
- _entries[timerIndex].doneFlag = false;
- _entries[timerIndex].field_13 = 0;
- _entries[timerIndex].dynamicHotspotIndex = -1;
- _entries[timerIndex].entries.count = 0;
- _entries[timerIndex].abortMode = _owner._abortTimersMode2;
+ _entries[seqIndex].active = true;
+ _entries[seqIndex].spriteListIndex = spriteListIndex;
+ _entries[seqIndex].flipped = flipped;
+ _entries[seqIndex].frameIndex = frameIndex;
+ _entries[seqIndex].frameStart = frameStart;
+ _entries[seqIndex].numSprites = numSprites;
+ _entries[seqIndex].animType = animType;
+ _entries[seqIndex].frameInc = frameInc;
+ _entries[seqIndex].depth = depth;
+ _entries[seqIndex].scale = scale;
+ _entries[seqIndex].nonFixed = nonFixed;
+ _entries[seqIndex].msgPos.x = msgX;
+ _entries[seqIndex].msgPos.y = msgY;
+ _entries[seqIndex].numTicks = numTicks;
+ _entries[seqIndex].extraTicks = extraTicks;
+
+ _entries[seqIndex].timeout = _madsVm->_currentTimer + delayTicks;
+
+ _entries[seqIndex].triggerCountdown = triggerCountdown;
+ _entries[seqIndex].doneFlag = false;
+ _entries[seqIndex].field_13 = 0;
+ _entries[seqIndex].dynamicHotspotIndex = -1;
+ _entries[seqIndex].entries.count = 0;
+ _entries[seqIndex].abortMode = _owner._abortTimersMode2;
for (int i = 0; i < 3; ++i)
- _entries[timerIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i];
+ _entries[seqIndex].actionNouns[i] = _madsVm->globals()->actionNouns[i];
- return timerIndex;
+ return seqIndex;
}
-void MadsSequenceList::remove(int timerIndex) {
- if (_entries[timerIndex].active) {
- if (_entries[timerIndex].dynamicHotspotIndex >= 0)
- _owner._dynamicHotspots.remove(_entries[timerIndex].dynamicHotspotIndex);
+void MadsSequenceList::remove(int seqIndex) {
+ if (_entries[seqIndex].active) {
+ if (_entries[seqIndex].dynamicHotspotIndex >= 0)
+ _owner._dynamicHotspots.remove(_entries[seqIndex].dynamicHotspotIndex);
}
- _entries[timerIndex].active = false;
- _owner._spriteSlots.deleteTimer(timerIndex);
+ _entries[seqIndex].active = false;
+ _owner._spriteSlots.deleteTimer(seqIndex);
}
-void MadsSequenceList::setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot) {
- MadsSequenceEntry &timerEntry = _entries[timerIndex];
- SpriteAsset &sprite = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex);
+void MadsSequenceList::setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot) {
+ MadsSequenceEntry &timerEntry = _entries[seqIndex];
+ SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex);
- // TODO: Figure out logic for spriteId value based on SPRITE_SLOT.field_0
- spriteSlot.spriteId = (0 /*field 0*/ == 1) ? -4 : 1;
- spriteSlot.timerIndex = timerIndex;
+ spriteSlot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE;
+ spriteSlot.seqIndex = seqIndex;
spriteSlot.spriteListIndex = timerEntry.spriteListIndex;
- spriteSlot.frameNumber = ((timerEntry.field_2 == 1) ? 0x8000 : 0) | timerEntry.frameIndex;
+ spriteSlot.frameNumber = (timerEntry.flipped ? 0x8000 : 0) | timerEntry.frameIndex;
spriteSlot.depth = timerEntry.depth;
spriteSlot.scale = timerEntry.scale;
- if (timerEntry.field_12 == 0) {
- spriteSlot.xp = timerEntry.width;
- spriteSlot.yp = timerEntry.height;
+ if (!timerEntry.nonFixed) {
+ spriteSlot.xp = timerEntry.msgPos.x;
+ spriteSlot.yp = timerEntry.msgPos.y;
} else {
- spriteSlot.xp = sprite.getFrame(timerEntry.frameIndex - 1)->x;
- spriteSlot.yp = sprite.getFrame(timerEntry.frameIndex - 1)->y;
+ spriteSlot.xp = spriteSet.getFrame(timerEntry.frameIndex - 1)->x;
+ spriteSlot.yp = spriteSet.getFrame(timerEntry.frameIndex - 1)->y;
}
}
-bool MadsSequenceList::loadSprites(int timerIndex) {
- MadsSequenceEntry &seqEntry = _entries[timerIndex];
+bool MadsSequenceList::loadSprites(int seqIndex) {
+ MadsSequenceEntry &seqEntry = _entries[seqIndex];
int slotIndex;
bool result = false;
int idx = -1;
- _owner._spriteSlots.deleteTimer(timerIndex);
+ _owner._spriteSlots.deleteTimer(seqIndex);
if (seqEntry.doneFlag) {
- remove(timerIndex);
+ remove(seqIndex);
return false;
}
@@ -599,7 +1362,7 @@ bool MadsSequenceList::loadSprites(int timerIndex) {
seqEntry.doneFlag = true;
} else if ((slotIndex = _owner._spriteSlots.getIndex()) >= 0) {
MadsSpriteSlot &spriteSlot = _owner._spriteSlots[slotIndex];
- setSpriteSlot(timerIndex, spriteSlot);
+ setSpriteSlot(seqIndex, spriteSlot);
int x2 = 0, y2 = 0;
@@ -621,7 +1384,7 @@ bool MadsSequenceList::loadSprites(int timerIndex) {
dynHotspot.bounds.top = MAX(y2 - height, 0);
dynHotspot.bounds.bottom = MIN(y2, 155) - dynHotspot.bounds.top;
- _owner._dynamicHotspots._flag = true;
+ _owner._dynamicHotspots._changed = true;
}
}
@@ -727,454 +1490,138 @@ void MadsSequenceList::delay(uint32 v1, uint32 v2) {
}
}
-//--------------------------------------------------------------------------
+void MadsSequenceList::setAnimRange(int seqIndex, int startVal, int endVal) {
+ MadsSequenceEntry &seqEntry = _entries[seqIndex];
+ SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex);
+ int numSprites = spriteSet.getCount();
+ int tempStart = startVal, tempEnd = endVal;
-MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this),
- _kernelMessages(*this) {
- _textSpacing = -1;
- _ticksAmount = 3;
- _newTimeout = 0;
- _abortTimers = 0;
- _abortTimers2 = 0;
- _abortTimersMode = ABORTMODE_0;
- _abortTimersMode2 = ABORTMODE_0;
-}
-
-void MadsView::refresh() {
- // Draw any sprites
- _spriteSlots.draw(_view);
-
- // Draw text elements onto the view
- _textDisplay.draw(_view);
-
- // Remove any sprite slots that are no longer needed
- _spriteSlots.cleanUp();
-
- // Deactivate any text display entries that are no longer needed
- _textDisplay.cleanUp();
-}
-
-/*--------------------------------------------------------------------------
- * MadsInterfaceView handles the user interface section at the bottom of
- * game screens in MADS games
- *--------------------------------------------------------------------------
- */
-
-MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm,
- Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) {
- _screenType = VIEWID_INTERFACE;
- _highlightedElement = -1;
- _topIndex = 0;
- _selectedObject = -1;
- _cheatKeyCtr = 0;
-
- _objectSprites = NULL;
- _objectPalData = NULL;
-
- /* Set up the rect list for screen elements */
- // Actions
- for (int i = 0; i < 10; ++i)
- _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2,
- ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2);
-
- // Scroller elements (up arrow, scroller, down arrow)
- _screenObjects.addRect(73, 160, 82, 167);
- _screenObjects.addRect(73, 168, 82, 190);
- _screenObjects.addRect(73, 191, 82, 198);
-
- // Inventory object names
- for (int i = 0; i < 5; ++i)
- _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8);
-
- // Full rectangle area for all vocab actions
- for (int i = 0; i < 5; ++i)
- _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8);
-}
-
-MadsInterfaceView::~MadsInterfaceView() {
- delete _objectSprites;
-}
-
-void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) {
- switch (newMode) {
- case ITEM_NORMAL:
- _vm->_font->setColors(4, 4, 0xff);
+ switch (startVal) {
+ case -2:
+ tempStart = numSprites;
break;
- case ITEM_HIGHLIGHTED:
- _vm->_font->setColors(5, 5, 0xff);
+ case -1:
+ tempStart = 1;
break;
- case ITEM_SELECTED:
- _vm->_font->setColors(6, 6, 0xff);
+ }
+
+ switch (endVal) {
+ case -2:
+ case 0:
+ tempEnd = numSprites;
+ break;
+ case -1:
+ tempEnd = 1;
+ break;
+ default:
+ tempEnd = numSprites;
break;
}
-}
-
-void MadsInterfaceView::initialise() {
- // Build up the inventory list
- _inventoryList.clear();
- for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) {
- MadsObject *obj = _madsVm->globals()->getObject(i);
- if (obj->roomNumber == PLAYER_INVENTORY)
- _inventoryList.push_back(i);
- }
+ seqEntry.frameStart = tempStart;
+ seqEntry.numSprites = tempEnd;
- // If the inventory has at least one object, select it
- if (_inventoryList.size() > 0)
- setSelectedObject(_inventoryList[0]);
+ seqEntry.frameIndex = (seqEntry.frameInc < 0) ? tempStart : tempEnd;
}
-void MadsInterfaceView::setSelectedObject(int objectNumber) {
- char resName[80];
-
- // Load inventory resource
- if (_objectSprites) {
- _vm->_palette->deleteRange(_objectPalData);
- delete _objectSprites;
- }
-
- // Check to make sure the object is in the inventory, and also visible on-screen
- int idx = _inventoryList.indexOf(objectNumber);
- if (idx == -1) {
- // Object wasn't found, so return
- _selectedObject = -1;
- return;
+void MadsSequenceList::scan() {
+ for (uint i = 0; i < _entries.size(); ++i) {
+ if (!_entries[i].active && (_entries[i].spriteListIndex != -1)) {
+ int idx = _owner._spriteSlots.getIndex();
+ setSpriteSlot(i, _owner._spriteSlots[idx]);
+ }
}
-
- // Found the object
- if (idx < _topIndex)
- _topIndex = idx;
- else if (idx >= (_topIndex + 5))
- _topIndex = MAX(0, idx - 4);
-
- _selectedObject = objectNumber;
- sprintf(resName, "*OB%.3dI.SS", objectNumber);
-
- Common::SeekableReadStream *data = _vm->res()->get(resName);
- _objectSprites = new SpriteAsset(_vm, data, data->size(), resName);
- _vm->res()->toss(resName);
-
- // Slot it into available palette space
- _objectPalData = _objectSprites->getRgbList();
- _vm->_palette->addRange(_objectPalData);
- _objectSprites->translate(_objectPalData, true);
-
- _objectFrameNumber = 0;
}
-void MadsInterfaceView::addObjectToInventory(int objectNumber) {
- if (_inventoryList.indexOf(objectNumber) == -1) {
- _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY;
- _inventoryList.push_back(objectNumber);
- }
-
- setSelectedObject(objectNumber);
+/**
+ * Sets the depth of the specified entry in the sequence list
+ */
+void MadsSequenceList::setDepth(int seqIndex, int depth) {
+ _entries[seqIndex].depth = depth;
}
-void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) {
- _vm->_font->setFont(FONT_INTERFACE_MADS);
- char buffer[100];
-
- // Check to see if any dialog is currently active
- bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL;
-
- // Highlighting logic for action list
- int actionIndex = 0;
- for (int x = 0; x < 2; ++x) {
- for (int y = 0; y < 5; ++y, ++actionIndex) {
- // Determine the font colour depending on whether an item is selected. Note that the first action,
- // 'Look', is always 'selected', even when another action is clicked on
- setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED :
- ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL));
-
- // Get the verb action and capitalise it
- const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex);
- strcpy(buffer, verbStr);
- if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
-
- // Display the verb
- const Common::Rect r(_screenObjects[actionIndex]);
- _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
- }
- }
-
- // Check for highlighting of the scrollbar controls
- if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) {
- // Highlight the control's borders
- const Common::Rect r(_screenObjects[_highlightedElement]);
- destSurface->frameRect(r, 5);
- }
-
- // Draw the horizontal line in the scroller representing the current top selected
- const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]);
- int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1);
- destSurface->setColor(4);
- destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP);
-
- // List inventory items
- for (uint i = 0; i < 5; ++i) {
- if ((_topIndex + i) >= _inventoryList.size())
- break;
-
- const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject(
- _inventoryList[_topIndex + i])->descId);
- strcpy(buffer, descStr);
- if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
-
- const Common::Rect r(_screenObjects[INVLIST_START + i]);
-
- // Set the highlighting of the inventory item
- if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED);
- else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED);
- else setFontMode(ITEM_NORMAL);
-
- // Write out it's description
- _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
- }
-
- // Handle the display of any currently selected object
- if (_objectSprites) {
- // Display object sprite. Note that the frame number isn't used directly, because it would result
- // in too fast an animation
- M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED);
- spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0);
-
- if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) {
- // If objects need to be animated, move to the next frame
- if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED))
- _objectFrameNumber = 0;
- }
-
- // List the vocab actions for the currently selected object
- MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
- int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
-
- for (int i = 0; i < obj->vocabCount; ++i) {
- const Common::Rect r(_screenObjects[VOCAB_START + i]);
-
- // Get the vocab description and capitalise it
- const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId);
- strcpy(buffer, descStr);
- if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
+//--------------------------------------------------------------------------
- // Set the highlighting and display the entry
- setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL);
- _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
- }
- }
+Animation::Animation(MadsM4Engine *vm): _vm(vm) {
}
-bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) {
- MadsAction &act = _madsVm->scene()->getAction();
-
- // If the mouse isn't being held down, then reset the repeated scroll timer
- if (eventType != MEVENT_LEFT_HOLD)
- _nextScrollerTicks = 0;
-
- // Handle various event types
- switch (eventType) {
- case MEVENT_MOVE:
- // If the cursor isn't in "wait mode", don't do any processing
- if (_vm->_mouse->getCursorNum() == CURSOR_WAIT)
- return true;
-
- // Ensure the cursor is the standard arrow
- _vm->_mouse->setCursorNum(CURSOR_ARROW);
-
- // Check if any interface element is currently highlighted
- _highlightedElement = _screenObjects.find(Common::Point(x, y));
-
- return true;
-
- case MEVENT_LEFT_CLICK:
- // Left mouse click
- {
- // Check if an inventory object was selected
- if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) {
- // Ensure there is an inventory item listed in that cell
- uint idx = _highlightedElement - INVLIST_START;
- if ((_topIndex + idx) < _inventoryList.size()) {
- // Set the selected object
- setSelectedObject(_inventoryList[_topIndex + idx]);
- }
- } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) {
- // A standard action was selected
- int verbId = kVerbLook + (_highlightedElement - ACTIONS_START);
- warning("Selected action #%d", verbId);
-
- } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) {
- // A vocab action was selected
- MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
- int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
- if (vocabIndex >= 0) {
- act._actionMode = ACTMODE_OBJECT;
- act._actionMode2 = ACTMODE2_2;
- act._flags1 = obj->vocabList[1].flags1;
- act._flags2 = obj->vocabList[1].flags2;
-
- act._currentHotspot = _selectedObject;
- act._articleNumber = act._flags2;
- }
- }
- }
- return true;
-
- case MEVENT_LEFT_HOLD:
- // Left mouse hold
- // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down
- if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) {
- if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) {
- // Handle scroll up/down action
- _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY;
-
- if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0))
- --_topIndex;
- if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1)))
- ++_topIndex;
- }
- }
- return true;
-
- case MEVENT_LEFT_DRAG:
- // Left mouse drag
- // Handle the the the scroller area that can be dragged to adjust the top displayed index
- if (_highlightedElement == SCROLL_SCROLLER) {
- // Calculate the new top index based on the Y position
- const Common::Rect r(_screenObjects[SCROLL_SCROLLER]);
- _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5),
- 0, (int)_inventoryList.size() - 1);
- }
- return true;
-
- case KEVENT_KEY:
- if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
- handleCheatKey(param1);
- handleKeypress(param1);
- return true;
-
- default:
- break;
- }
-
- return false;
+Animation::~Animation() {
}
-bool MadsInterfaceView::handleCheatKey(int32 keycode) {
- switch (keycode) {
- case Common::KEYCODE_SPACE:
- // TODO: Move player to current destination
- return true;
-
- case Common::KEYCODE_t | (Common::KEYCODE_LALT):
- case Common::KEYCODE_t | (Common::KEYCODE_RALT):
- {
- // Teleport to room
- //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE);
-
-
- return true;
- }
+//--------------------------------------------------------------------------
- default:
- break;
- }
+MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this),
+ _kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this),
+ _screenObjects(*this), _action(*this) {
+
+ _textSpacing = -1;
+ _newTimeout = 0;
+ _abortTimers = 0;
+ _abortTimers2 = 0;
+ _abortTimersMode = ABORTMODE_0;
+ _abortTimersMode2 = ABORTMODE_0;
- return false;
+ _depthSurface = NULL;
+ _bgSurface = NULL;
+ _viewport = NULL;
+ _sceneAnimation = new MadsAnimation(_vm, this);
}
-const char *CHEAT_SEQUENCE = "widepipe";
+MadsView::~MadsView() {
+ delete _sceneAnimation;
+ delete _viewport;
+}
-bool MadsInterfaceView::handleKeypress(int32 keycode) {
- int flags = keycode >> 24;
- int kc = keycode & 0xffff;
+void MadsView::refresh() {
+ if (!_viewport)
+ setViewport(_view->bounds());
- // Capitalise the letter if necessary
- if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) {
- if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) {
- ++_cheatKeyCtr;
- if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
- Dialog::display(_vm, 22, cheatingEnabledDesc);
- return true;
- } else {
- _cheatKeyCtr = 0;
- }
- }
+ // Draw any sprites
+ _dirtyAreas.clear();
+ _spriteSlots.drawBackground();
- // Handle the various keys
- if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) {
- // Game menu
- _madsVm->globals()->dialogType = DIALOG_GAME_MENU;
- leaveScene();
- return false;
- } else if (flags & Common::KBD_CTRL) {
- // Handling of the different control key combinations
- switch (kc) {
- case Common::KEYCODE_i:
- // Mouse to inventory
- warning("TODO: Mouse to inventory");
- break;
+ // Process dirty areas
+ _textDisplay.setDirtyAreas();
- case Common::KEYCODE_k:
- // Toggle hotspots
- warning("TODO: Toggle hotspots");
- break;
+ // Merge any identified dirty areas
+ _dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
+
+ // Copy dirty areas to the main display surface
+ _dirtyAreas.copy(_viewport, _bgSurface, _posAdjust);
- case Common::KEYCODE_p:
- // Player stats
- warning("TODO: Player stats");
- break;
+ // Handle dirty areas for foreground objects
+ _spriteSlots.setDirtyAreas();
+ _textDisplay.setDirtyAreas2();
+ _dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
- case Common::KEYCODE_q:
- // Quit game
- break;
+ // Draw foreground sprites
+ _spriteSlots.drawForeground(_viewport);
- case Common::KEYCODE_s:
- // Activate sound
- warning("TODO: Activate sound");
- break;
+ // Draw text elements onto the view
+ _textDisplay.draw(_viewport);
- case Common::KEYCODE_u:
- // Rotate player
- warning("TODO: Rotate player");
- break;
+ // Remove any sprite slots that are no longer needed
+ _spriteSlots.cleanUp();
- case Common::KEYCODE_v: {
- // Release version
- Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr);
- _vm->_viewManager->addView(dlg);
- _vm->_viewManager->moveToFront(dlg);
- return false;
- }
+ // Deactivate any text display entries that are no longer needed
+ _textDisplay.cleanUp();
+}
- default:
- break;
- }
- } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) {
- // Quit Game
+void MadsView::update() {
+ _sequenceList.tick();
+ _kernelMessages.update();
+}
- } else {
- // Standard keypresses
- switch (kc) {
- case Common::KEYCODE_F2:
- // Save game
- _madsVm->globals()->dialogType = DIALOG_SAVE;
- leaveScene();
- break;
- case Common::KEYCODE_F3:
- // Restore game
- _madsVm->globals()->dialogType = DIALOG_RESTORE;
- leaveScene();
- break;
- }
- }
-//DIALOG_OPTIONS
- return false;
+void MadsView::clearLists() {
+ _textDisplay.clear();
+ _kernelMessages.clear();
+ _spriteSlots.clear();
}
-void MadsInterfaceView::leaveScene() {
- // Close the scene
- View *view = _madsVm->_viewManager->getView(VIEWID_SCENE);
- _madsVm->_viewManager->deleteView(view);
+void MadsView::setViewport(const Common::Rect &bounds) {
+ delete _viewport;
+ _viewport = new M4Surface(bounds.width(), bounds.height(), _view->getBasePtr(bounds.left, bounds.top),
+ _view->getPitch());
}
} // End of namespace M4
diff --git a/engines/m4/mads_views.h b/engines/m4/mads_views.h
index 926702a80b..c93d0beda3 100644
--- a/engines/m4/mads_views.h
+++ b/engines/m4/mads_views.h
@@ -34,18 +34,87 @@
namespace M4 {
-#define MADS_SURFACE_HEIGHT 156
-#define MADS_SCREEN_HEIGHT 200
-#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2)
-
class MadsView;
+enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6};
+enum MAdsActionMode2 {ACTMODE2_0 = 0, ACTMODE2_2 = 2, ACTMODE2_4 = 4, ACTMODE2_5 = 5};
+
+struct ActionDetails {
+ int verbId;
+ int objectNameId;
+ int indirectObjectId;
+};
+
+struct MadsActionSavedFields {
+ int articleNumber;
+ int actionMode;
+ int actionMode2;
+ bool lookFlag;
+ int selectedRow;
+};
+
+class MadsAction {
+private:
+ MadsView &_owner;
+ char _statusText[100];
+ char _dialogTitle[100];
+
+ void appendVocab(int vocabId, bool capitalise = false);
+public:
+ ActionDetails _action, _activeAction;
+ int _currentAction;
+ int8 _flags1, _flags2;
+ MadsActionMode _actionMode;
+ MAdsActionMode2 _actionMode2;
+ int _articleNumber;
+ bool _lookFlag;
+ int _selectedRow;
+ bool _textChanged;
+ int _selectedAction;
+ bool _startWalkFlag;
+ int _statusTextIndex;
+ int _hotspotId;
+ MadsActionSavedFields _savedFields;
+ bool _walkFlag;
+
+ // Unknown fields
+ int16 _v86F3A;
+ int16 _v86F42;
+ int16 _v86F4E;
+ bool _v86F4A;
+ int16 _v86F4C;
+ int _v83338;
+ bool _inProgress;
+ bool _v8453A;
+
+public:
+ MadsAction(MadsView &owner);
+
+ void clear();
+ void set();
+ const char *statusText() const { return _statusText; }
+ void refresh();
+ void startAction();
+ void checkAction();
+ bool isAction(int verbId, int objectNameId = 0, int indirectObjectId = 0);
+};
+
enum AbortTimerMode {ABORTMODE_0 = 0, ABORTMODE_1 = 1, ABORTMODE_2 = 2};
+class SpriteSlotSubset {
+public:
+ int spriteListIndex;
+ int frameNumber;
+ int xp;
+ int yp;
+ int depth;
+ int scale;
+};
+
class MadsSpriteSlot {
public:
- int spriteId;
- int timerIndex;
+ int spriteType;
+ int seqIndex;
int spriteListIndex;
int frameNumber;
int xp;
@@ -54,20 +123,27 @@ public:
int scale;
MadsSpriteSlot() { }
+
+ bool operator==(const SpriteSlotSubset &other) const;
+ void copy(const SpriteSlotSubset &other);
};
#define SPRITE_SLOTS_SIZE 50
-typedef Common::Array<Common::SharedPtr<SpriteAsset> > SpriteList;
+enum SpriteIdSpecial {
+ BACKGROUND_SPRITE = -4, FULL_SCREEN_REFRESH = -2, EXPIRED_SPRITE = -1, SPRITE_ZERO = 0, FOREGROUND_SPRITE = 1
+};
class MadsSpriteSlots {
private:
+ MadsView &_owner;
Common::Array<MadsSpriteSlot> _entries;
- SpriteList _sprites;
+ Common::Array<SpriteAsset *> _sprites;
public:
int startIndex;
- MadsSpriteSlots();
+ MadsSpriteSlots(MadsView &owner);
+ ~MadsSpriteSlots();
MadsSpriteSlot &operator[](int idx) {
assert(idx < SPRITE_SLOTS_SIZE);
@@ -75,18 +151,20 @@ public:
}
SpriteAsset &getSprite(int idx) {
assert(idx < (int)_sprites.size());
- return *_sprites[idx].get();
+ return *_sprites[idx];
}
int getIndex();
- int addSprites(const char *resName);
- void clear() {
- startIndex = 0;
- _sprites.clear();
- }
- void deleteTimer(int timerIndex);
+ int addSprites(const char *resName, bool suppressErrors = false, int flags = 0);
+ int addSprites(SpriteAsset *spriteSet);
+ void deleteSprites(int listIndex);
+ void clear();
+ void deleteTimer(int seqIndex);
- void draw(View *view);
+ void drawBackground();
+ void drawForeground(M4Surface *viewport);
+ void setDirtyAreas();
+ void fullRefresh();
void cleanUp();
};
@@ -108,9 +186,10 @@ public:
class MadsTextDisplay {
private:
+ MadsView &_owner;
Common::Array<MadsTextDisplayEntry> _entries;
public:
- MadsTextDisplay();
+ MadsTextDisplay(MadsView &owner);
MadsTextDisplayEntry &operator[](int idx) {
assert(idx < TEXT_DISPLAY_SIZE);
@@ -124,16 +203,19 @@ public:
int add(int xp, int yp, uint fontColour, int charSpacing, const char *msg, Font *font);
void clear();
- void draw(View *view);
+ void draw(M4Surface *view);
+ void setDirtyAreas();
+ void setDirtyAreas2();
void cleanUp();
};
#define TIMED_TEXT_SIZE 10
-#define TEXT_4A_SIZE 30
+#define INDEFINITE_TIMEOUT 9999999
-enum KernelMessageFlags {KMSG_1 = 1, KMSG_2 = 2, KMSG_4 = 4, KMSG_8 = 8, KMSG_20 = 0x20, KMSG_40 = 0x40, KMSG_ACTIVE = 0x80};
+enum KernelMessageFlags {KMSG_QUOTED = 1, KMSG_PLAYER_TIMEOUT = 2, KMSG_SEQ_ENTRY = 4, KMSG_SCROLL = 8, KMSG_RIGHT_ALIGN = 0x10,
+ KMSG_CENTER_ALIGN = 0x20, KMSG_EXPIRE = 0x40, KMSG_ACTIVE = 0x80};
-class MadsKernelMessageListEntry {
+class MadsKernelMessageEntry {
public:
uint8 flags;
int sequenceIndex;
@@ -144,31 +226,39 @@ public:
Common::Point position;
int textDisplayIndex;
int msgOffset;
- int field_E;
+ int numTicks;
uint32 frameTimer2;
uint32 frameTimer;
uint32 timeout;
- bool field_1C;
+ int abortTimers;
AbortTimerMode abortMode;
uint16 actionNouns[3];
- const char *msg;
+ char msg[100];
+
+ MadsKernelMessageEntry() {
+ flags = 0;
+ }
};
class MadsKernelMessageList {
private:
MadsView &_owner;
- Common::Array<MadsKernelMessageListEntry> _entries;
+ Common::Array<MadsKernelMessageEntry> _entries;
Font *_talkFont;
public:
+ int word_8469E;
+public:
MadsKernelMessageList(MadsView &owner);
void clear();
- int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg);
- int addQuote(int quoteId, int v2, uint32 timeout);
- void unk1(int msgIndex, int v1, int v2);
+ int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg);
+ int addQuote(int quoteId, int abortTimers, uint32 timeout);
+ void scrollMessage(int msgIndex, int numTicks, bool quoted);
void setSeqIndex(int msgIndex, int seqIndex);
void remove(int msgIndex);
void reset();
+ void update();
+ void processText(int msgIndex);
};
class ScreenObjectEntry {
@@ -184,10 +274,20 @@ public:
class ScreenObjects {
private:
+ MadsView &_owner;
Common::Array<ScreenObjectEntry> _entries;
public:
- ScreenObjects() {}
+ int _v832EC;
+ int _v7FECA;
+ int _v7FED6;
+ int _v8332A;
+ int _yp;
+ int _v8333C;
+ int _selectedObject;
+ int _category;
+ int _objectIndex;
+ ScreenObjects(MadsView &owner);
ScreenObjectEntry &operator[](uint idx) {
assert(idx <= _entries.size());
return _entries[idx - 1];
@@ -199,12 +299,13 @@ public:
int scan(int xp, int yp, int layer);
int scanBackwards(int xp, int yp, int layer);
void setActive(int category, int idx, bool active);
+ void check(bool scanFlag, bool mouseClick);
};
class DynamicHotspot {
public:
bool active;
- int timerIndex;
+ int seqIndex;
Common::Rect bounds;
Common::Point pos;
int facing;
@@ -224,16 +325,54 @@ private:
Common::Array<DynamicHotspot> _entries;
int _count;
public:
- bool _flag;
+ bool _changed;
public:
MadsDynamicHotspots(MadsView &owner);
DynamicHotspot &operator[](uint idx) { return _entries[idx]; }
- int add(int descId, int field14, int timerIndex, const Common::Rect &bounds);
+ int add(int descId, int field14, int seqIndex, const Common::Rect &bounds);
int setPosition(int index, int xp, int yp, int facing);
int set17(int index, int v);
void remove(int index);
void reset();
+ void refresh() {
+ // TODO
+ }
+};
+
+class MadsDirtyArea {
+public:
+ Common::Rect bounds;
+ Common::Rect bounds2;
+ bool textActive;
+ bool active;
+
+ MadsDirtyArea() { active = false; }
+ void setArea(int width, int height, int maxWidth, int maxHeight);
+};
+
+#define DIRTY_AREAS_SIZE 90
+#define DIRTY_AREAS_TEXT_DISPLAY_IDX 50
+
+class MadsDirtyAreas {
+private:
+ MadsView &_owner;
+ Common::Array<MadsDirtyArea> _entries;
+public:
+ MadsDirtyAreas(MadsView &owner);
+
+ MadsDirtyArea &operator[](uint idx) {
+ assert(idx < _entries.size());
+ return _entries[idx];
+ }
+
+ void setSpriteSlot(int dirtyIdx, const MadsSpriteSlot &spriteSlot);
+ void setTextDisplay(int dirtyIdx, const MadsTextDisplayEntry &textDisplay);
+ void merge(int startIndex, int count);
+ bool intersects(int idx1, int idx2);
+ void mergeAreas(int idx1, int idx2);
+ void copy(M4Surface *dest, M4Surface *src, const Common::Point &posAdjust);
+ void clear();
};
enum SpriteAnimType {ANIMTYPE_CYCLED = 1, ANIMTYPE_REVERSIBLE = 2};
@@ -252,8 +391,7 @@ struct MadsSequenceSubEntries {
struct MadsSequenceEntry {
int8 active;
int8 spriteListIndex;
-
- int field_2;
+ bool flipped;
int frameIndex;
int frameStart;
@@ -266,12 +404,10 @@ struct MadsSequenceEntry {
int scale;
int dynamicHotspotIndex;
- int field_12;
+ bool nonFixed;
int field_13;
- int width;
- int height;
-
+ Common::Point msgPos;
int triggerCountdown;
bool doneFlag;
MadsSequenceSubEntries entries;
@@ -295,88 +431,66 @@ public:
MadsSequenceEntry &operator[](int index) { return _entries[index]; }
void clear();
bool addSubEntry(int index, SequenceSubEntryMode mode, int frameIndex, int abortVal);
- int add(int spriteListIndex, int v0, int v1, int triggerCountdown, int delayTicks, int extraTicks, int numTicks,
- int height, int width, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType,
- int numSprites, int frameStart);
- void remove(int timerIndex);
- void setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot);
- bool loadSprites(int timerIndex);
+ int add(int spriteListIndex, bool flipped, int frameIndex, int triggerCountdown, int delayTicks,
+ int extraTicks, int numTicks, int msgX, int msgY, bool nonFixed, char scale, uint8 depth,
+ int frameInc, SpriteAnimType animType, int numSprites, int frameStart);
+ void remove(int seqIndex);
+ void setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot);
+ bool loadSprites(int seqIndex);
void tick();
void delay(uint32 v1, uint32 v2);
+ void setAnimRange(int seqIndex, int startVal, int endVal);
+ void scan();
+ void setDepth(int seqIndex, int depth);
};
+class Animation {
+protected:
+ MadsM4Engine *_vm;
+public:
+ Animation(MadsM4Engine *vm);
+ virtual ~Animation();
+ virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *surface, M4Surface *depthSurface) = 0;
+ virtual void load(const Common::String &filename, int v0) = 0;
+ virtual void update() = 0;
+ virtual void setCurrentFrame(int frameNumber) = 0;
+ virtual int getCurrentFrame() = 0;
+};
+
+
class MadsView {
private:
View *_view;
public:
+ Animation *_sceneAnimation;
MadsSpriteSlots _spriteSlots;
MadsTextDisplay _textDisplay;
MadsKernelMessageList _kernelMessages;
ScreenObjects _screenObjects;
MadsDynamicHotspots _dynamicHotspots;
MadsSequenceList _sequenceList;
+ MadsDirtyAreas _dirtyAreas;
+ MadsAction _action;
int _textSpacing;
- int _ticksAmount;
uint32 _newTimeout;
int _abortTimers;
int8 _abortTimers2;
AbortTimerMode _abortTimersMode;
AbortTimerMode _abortTimersMode2;
+ Common::Point _posAdjust;
+
+ M4Surface *_depthSurface;
+ M4Surface *_bgSurface;
+ M4Surface *_viewport;
public:
MadsView(View *view);
+ ~MadsView();
void refresh();
-};
-
-#define CHEAT_SEQUENCE_MAX 8
-
-class IntegerList : public Common::Array<int> {
-public:
- int indexOf(int v) {
- for (uint i = 0; i < size(); ++i)
- if (operator [](i) == v)
- return i;
- return -1;
- }
-};
-
-enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED};
-
-enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12,
- INVLIST_START = 13, VOCAB_START = 18};
-
-class MadsInterfaceView : public GameInterfaceView {
-private:
- IntegerList _inventoryList;
- RectList _screenObjects;
- int _highlightedElement;
- int _topIndex;
- uint32 _nextScrollerTicks;
- int _cheatKeyCtr;
-
- // Object display fields
- int _selectedObject;
- SpriteAsset *_objectSprites;
- RGBList *_objectPalData;
- int _objectFrameNumber;
-
- void setFontMode(InterfaceFontMode newMode);
- bool handleCheatKey(int32 keycode);
- bool handleKeypress(int32 keycode);
- void leaveScene();
-public:
- MadsInterfaceView(MadsM4Engine *vm);
- ~MadsInterfaceView();
-
- virtual void initialise();
- virtual void setSelectedObject(int objectNumber);
- virtual void addObjectToInventory(int objectNumber);
- int getSelectedObject() { return _selectedObject; }
- int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; }
-
- void onRefresh(RectList *rects, M4Surface *destSurface);
- bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents);
+ void update();
+ void clearLists();
+ void setViewport(const Common::Rect &bounds);
};
}
diff --git a/engines/m4/midi.cpp b/engines/m4/midi.cpp
index 78fe0d6bd6..2c767fdf5a 100644
--- a/engines/m4/midi.cpp
+++ b/engines/m4/midi.cpp
@@ -109,7 +109,7 @@ void MidiPlayer::send(uint32 b) {
else if ((b & 0xFFF0) == 0x007BB0) {
//Only respond to All Notes Off if this channel
//has currently been allocated
- if (_channel[b & 0x0F])
+ if (!_channel[b & 0x0F])
return;
}
diff --git a/engines/m4/module.mk b/engines/m4/module.mk
index 1b08ea2188..f60757ba3b 100644
--- a/engines/m4/module.mk
+++ b/engines/m4/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS = \
mads_anim.o \
mads_logic.o \
mads_menus.o \
+ mads_player.o \
mads_scene.o \
mads_views.o \
midi.o \
diff --git a/engines/m4/rails.cpp b/engines/m4/rails.cpp
index fbad6995eb..11b9bcdbb9 100644
--- a/engines/m4/rails.cpp
+++ b/engines/m4/rails.cpp
@@ -179,7 +179,7 @@ long SqrtF16(long n) {
uint32 r = 0, s;
uint32 v = (uint32)n;
- for (int i = 15; i <= 0; i--) {
+ for (int i = 15; i >= 0; --i) {
s = r + (1L << i * 2);
r >>= 1;
if (s <= v) {
diff --git a/engines/m4/rails.h b/engines/m4/rails.h
index a7add5a8eb..e3183c243f 100644
--- a/engines/m4/rails.h
+++ b/engines/m4/rails.h
@@ -93,6 +93,8 @@ private:
bool isLineWalkable(int x0, int y0, int x1, int y1);
};
+long SqrtF16(long n);
+
} // End of namespace M4
#endif
diff --git a/engines/m4/scene.cpp b/engines/m4/scene.cpp
index 15c68f276c..8457f2087a 100644
--- a/engines/m4/scene.cpp
+++ b/engines/m4/scene.cpp
@@ -44,7 +44,7 @@ Scene::Scene(MadsM4Engine *vm, SceneResources *res): View(vm, Common::Rect(0, 0,
_screenType = VIEWID_SCENE;
_sceneResources->hotspots = new HotSpotList();
- _sceneResources->props = new HotSpotList();
+ _sceneResources->dynamicHotspots = new HotSpotList();
_backgroundSurface = new M4Surface();
_walkSurface = new M4Surface();
_palData = NULL;
@@ -55,11 +55,13 @@ Scene::Scene(MadsM4Engine *vm, SceneResources *res): View(vm, Common::Rect(0, 0,
Scene::~Scene() {
leaveScene();
+ _vm->_scene = NULL;
}
void Scene::loadScene(int sceneNumber) {
_previousScene = _currentScene;
_currentScene = sceneNumber;
+ _nextScene = sceneNumber;
}
void Scene::leaveScene() {
@@ -122,14 +124,14 @@ void Scene::showHotSpots() {
HotSpot *currentHotSpot;
// hotspots (green)
- for (i = 0; i < _sceneResources->hotspotCount; i++) {
+ for (i = 0; i < _sceneResources->hotspots->size(); i++) {
currentHotSpot = _sceneResources->hotspots->get(i);
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->GREEN);
}
- // props (red)
- for (i = 0; i < _sceneResources->propsCount; i++) {
- currentHotSpot = _sceneResources->props->get(i);
+ // Dynamic hotspots (red)
+ for (i = 0; i < _sceneResources->dynamicHotspots->size(); i++) {
+ currentHotSpot = _sceneResources->dynamicHotspots->get(i);
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->RED);
}
}
@@ -153,8 +155,21 @@ void Scene::showCodes() {
colors[255 * 4 + 2] = 255;
_vm->_palette->setPalette(colors, 0, 256);
} else {
- // For MADS, simply copy the walk data to the background, in whatever current palette is active
+ // MADS handling
+
+ // copy the walk data to the background, in whatever current palette is active
_walkSurface->copyTo(_backgroundSurface);
+
+ // Show all the scene's walk nodes
+ SceneNodeList &nodeList = _madsVm->scene()->getSceneResources()._nodes;
+ _backgroundSurface->setColour(_madsVm->_palette->WHITE);
+ for (uint i = 0; i < nodeList.size() - 2; ++i) {
+ // Draw a little cross at the node's position
+ _backgroundSurface->hLine(nodeList[i].pt.x - 2, nodeList[i].pt.x + 2, nodeList[i].pt.y);
+ _backgroundSurface->vLine(nodeList[i].pt.x, nodeList[i].pt.y - 2, nodeList[i].pt.y + 2);
+ }
+
+ ((MadsScene *)this)->_spriteSlots.fullRefresh();
}
}
@@ -183,7 +198,7 @@ bool Scene::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &cap
rightClick(x, y);
break;
case MEVENT_MOVE:
- checkHotspotAtMousePos(x, y);
+ mouseMove(x, y);
break;
default:
return false;
diff --git a/engines/m4/scene.h b/engines/m4/scene.h
index 633a34b549..9262a7c828 100644
--- a/engines/m4/scene.h
+++ b/engines/m4/scene.h
@@ -53,17 +53,16 @@ enum MADSVerbs {
kVerbPull = 10,
kVerbClose = 11,
kVerbThrow = 12,
- kVerbWalkTo = 13
+ kVerbWalkTo = 13,
+ kVerbLookAt = 209
};
class SceneResources {
public:
char artBase[MAX_CHK_FILENAME_SIZE];
char pictureBase[MAX_CHK_FILENAME_SIZE];
- int32 hotspotCount;
HotSpotList *hotspots;
- int32 propsCount;
- HotSpotList *props;
+ HotSpotList *dynamicHotspots;
int32 frontY, backY;
int32 frontScale, backScale;
int16 depthTable[16];
@@ -80,6 +79,7 @@ private:
protected:
int _currentScene;
int _previousScene;
+ int _nextScene;
GameInterfaceView *_interfaceSurface;
M4Surface *_backgroundSurface;
M4Surface *_walkSurface;
@@ -95,7 +95,7 @@ public:
virtual void leaveScene();
virtual void loadSceneCodes(int sceneNumber, int index = 0) = 0;
virtual void show();
- virtual void checkHotspotAtMousePos(int x, int y) = 0;
+ virtual void mouseMove(int x, int y) = 0;
virtual void leftClick(int x, int y) = 0;
virtual void rightClick(int x, int y) = 0;
virtual void update() = 0;
diff --git a/engines/m4/sound.cpp b/engines/m4/sound.cpp
index 69ab8c0516..e0fbd2f7a9 100644
--- a/engines/m4/sound.cpp
+++ b/engines/m4/sound.cpp
@@ -197,20 +197,20 @@ void Sound::loadDSRFile(const char *fileName) {
//printf("DSR has %i entries\n", _dsrFile.entryCount);
for (int i = 0; i < _dsrFile.entryCount; i++) {
- DSREntry* newEntry = new DSREntry();
- newEntry->frequency = fileStream->readUint16LE();
- newEntry->channels = fileStream->readUint32LE();
- newEntry->compSize = fileStream->readUint32LE();
- newEntry->uncompSize = fileStream->readUint32LE();
- newEntry->offset = fileStream->readUint32LE();
+ DSREntry newEntry;
+ newEntry.frequency = fileStream->readUint16LE();
+ newEntry.channels = fileStream->readUint32LE();
+ newEntry.compSize = fileStream->readUint32LE();
+ newEntry.uncompSize = fileStream->readUint32LE();
+ newEntry.offset = fileStream->readUint32LE();
_dsrFile.dsrEntries.push_back(newEntry);
/*
printf("%i: ", i);
printf("frequency: %i ", newEntry->frequency);
printf("channels: %i ", newEntry->channels);
- printf("comp: %i ", newEntry->compSize);
- printf("uncomp: %i ", newEntry->uncompSize);
+ printf("comp: %i ", newEntry.compSize);
+ printf("uncomp: %i ", newEntry.uncompSize);
printf("offset: %i ", newEntry->offset);
printf("\n");
*/
@@ -225,9 +225,7 @@ void Sound::unloadDSRFile() {
if (!_dsrFileLoaded)
return;
- for (int i = 0; i < _dsrFile.entryCount; i++) {
- _dsrFile.dsrEntries.remove_at(0);
- }
+ _dsrFile.dsrEntries.clear();
_dsrFile.entryCount = 0;
strcpy(_dsrFile.fileName, "");
@@ -251,28 +249,28 @@ void Sound::playDSRSound(int soundIndex, int volume, bool loop) {
// Get sound data
FabDecompressor fab;
- byte *compData = new byte[_dsrFile.dsrEntries[soundIndex]->compSize];
- byte *buffer = new byte[_dsrFile.dsrEntries[soundIndex]->uncompSize];
+ byte *compData = new byte[_dsrFile.dsrEntries[soundIndex].compSize];
+ byte *buffer = new byte[_dsrFile.dsrEntries[soundIndex].uncompSize];
Common::SeekableReadStream *fileStream = _vm->res()->get(_dsrFile.fileName);
- fileStream->seek(_dsrFile.dsrEntries[soundIndex]->offset, SEEK_SET);
- fileStream->read(compData, _dsrFile.dsrEntries[soundIndex]->compSize);
+ fileStream->seek(_dsrFile.dsrEntries[soundIndex].offset, SEEK_SET);
+ fileStream->read(compData, _dsrFile.dsrEntries[soundIndex].compSize);
_vm->res()->toss(_dsrFile.fileName);
- fab.decompress(compData, _dsrFile.dsrEntries[soundIndex]->compSize,
- buffer, _dsrFile.dsrEntries[soundIndex]->uncompSize);
+ fab.decompress(compData, _dsrFile.dsrEntries[soundIndex].compSize,
+ buffer, _dsrFile.dsrEntries[soundIndex].uncompSize);
// Play sound
Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
Audio::makeRawStream(buffer,
- _dsrFile.dsrEntries[soundIndex]->uncompSize,
- _dsrFile.dsrEntries[soundIndex]->frequency, Audio::FLAG_UNSIGNED),
+ _dsrFile.dsrEntries[soundIndex].uncompSize,
+ _dsrFile.dsrEntries[soundIndex].frequency, Audio::FLAG_UNSIGNED),
loop ? 0 : 1);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &handle->handle, stream, -1, volume);
/*
// Dump the sound file
FILE *destFile = fopen("sound.raw", "wb");
- fwrite(_dsrFile.dsrEntries[soundIndex]->data, _dsrFile.dsrEntries[soundIndex]->uncompSize, 1, destFile);
+ fwrite(_dsrFile.dsrEntries[soundIndex]->data, _dsrFile.dsrEntries[soundIndex].uncompSize, 1, destFile);
fclose(destFile);
*/
}
diff --git a/engines/m4/sound.h b/engines/m4/sound.h
index 7d442a73cc..5587810506 100644
--- a/engines/m4/sound.h
+++ b/engines/m4/sound.h
@@ -65,7 +65,7 @@ struct DSREntry {
struct DSRFile {
char fileName[20];
int entryCount;
- Common::Array<DSREntry *> dsrEntries;
+ Common::Array<DSREntry> dsrEntries;
};
class MadsM4Engine;
diff --git a/engines/m4/sprite.cpp b/engines/m4/sprite.cpp
index 0ff4bec855..641b93baea 100644
--- a/engines/m4/sprite.cpp
+++ b/engines/m4/sprite.cpp
@@ -121,60 +121,89 @@ void M4Sprite::loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int
// TODO: The sprite outlines (pixel value 0xFD) are not shown
void M4Sprite::loadMadsSprite(Common::SeekableReadStream* source) {
- byte *outp, *lineStart;
- bool newLine = false;
+ bool spriteEnd = false;
- outp = getBasePtr();
- lineStart = getBasePtr();
+ // Set entire sprite contents to transparent pixels
+ fillRect(bounds(), TRANSPARENT_COLOUR_INDEX);
- while (1) {
- byte cmd1, cmd2, count, pixel;
-
- if (newLine) {
- outp = lineStart + w;
- lineStart = outp;
- newLine = false;
- }
-
- cmd1 = source->readByte();
+ // Major line loop
+ for (int yp = 0; yp < h; ++yp) {
+ byte *destP = getBasePtr(0, yp);
+ bool newLine = false;
+ byte cmd = source->readByte();
+ int x2 = 0;
- if (cmd1 == 0xFC)
+ if (cmd == 0xfc) {
+ // End of entire sprite
+ spriteEnd = true;
break;
- else if (cmd1 == 0xFF)
+ } else if (cmd == 0xff) {
+ // The entire line is empty
newLine = true;
- else if (cmd1 == 0xFD) {
- while (!newLine) {
- count = source->readByte();
- if (count == 0xFF) {
+ } else if (cmd == 0xFD) {
+ // Lines contains only run lenghs of pixels
+ while (x2 < w) {
+ cmd = source->readByte();
+ if (cmd == 0xff) {
+ // End of line reached
newLine = true;
- } else {
- pixel = source->readByte();
- while (count--)
- *outp++ = (pixel == 0xFD) ? 0 : pixel;
+ break;
+ }
+
+ byte v = source->readByte();
+ while (cmd-- > 0) {
+ if (x2 < w)
+ *destP++ = (v == 0xFD) ? TRANSPARENT_COLOUR_INDEX : v;
+ ++x2;
}
}
} else {
- while (!newLine) {
- cmd2 = source->readByte();
- if (cmd2 == 0xFF) {
+ // Line intermixes run lengths with individual pixels
+ while (x2 < w) {
+ cmd = source->readByte();
+ if (cmd == 0xff) {
+ // End of line reached
newLine = true;
- } else if (cmd2 == 0xFE) {
- count = source->readByte();
- pixel = source->readByte();
- while (count--)
- *outp++ = (pixel == 0xFD) ? 0 : pixel;
+ break;
+ }
+
+ if (cmd == 0xFE) {
+ // Handle repeated sequence
+ cmd = source->readByte();
+ byte v = source->readByte();
+ while (cmd-- > 0) {
+ if (x2 < w) {
+ *destP++ = (v == 0xFD) ? TRANSPARENT_COLOUR_INDEX : v;
+ }
+ ++x2;
+ }
} else {
- *outp++ = (cmd2 == 0xFD) ? 0 : cmd2;
+ // Handle writing out single pixel
+ *destP++ = (cmd == 0xFD) ? TRANSPARENT_COLOUR_INDEX : cmd;
+ ++x2;
}
}
}
+
+ // Check if we need to scan forward to find the end of the line
+ if (!newLine) {
+ do {
+ if (source->eos()) {
+ warning("M4Sprite::loadMadsSprite: unexpected end of data");
+ break;
+ }
+ } while (source->readByte() != 0xff);
+ }
+ }
+
+ if (!spriteEnd) {
+ byte v = source->readByte();
+ assert(v == 0xFC);
}
}
-byte M4Sprite::getTransparentColor() const {
- // FIXME: We assume that the transparent color is the color of the
- // top left pixel.
- return *getBasePtr(0, 0);
+byte M4Sprite::getTransparencyIndex() const {
+ return TRANSPARENT_COLOUR_INDEX;
}
} // End of namespace M4
diff --git a/engines/m4/sprite.h b/engines/m4/sprite.h
index 022f3bbc4f..d4e5502efd 100644
--- a/engines/m4/sprite.h
+++ b/engines/m4/sprite.h
@@ -115,19 +115,7 @@ public:
void loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY);
void loadMadsSprite(Common::SeekableReadStream* source);
- void draw1(M4Surface *destSurface, int scale, int depth, int xp, int yp) {
- // TODO: Properly implement drawing
- copyTo(destSurface, xp, yp, 0);
- }
- void draw2(M4Surface *destSurface, int depth, int xp, int yp) {
- // TODO: Properly implement drawing
- copyTo(destSurface, xp, yp, 0);
- }
- void draw3(M4Surface *destSurface, int xp, int yp) {
- // TODO: Properly implement drawing
- copyTo(destSurface, xp, yp, 0);
- }
- byte getTransparentColor() const;
+ byte getTransparencyIndex() const;
protected:
};
diff --git a/engines/m4/viewmgr.h b/engines/m4/viewmgr.h
index 16c3d6ecc3..211e6087f4 100644
--- a/engines/m4/viewmgr.h
+++ b/engines/m4/viewmgr.h
@@ -42,6 +42,19 @@ namespace M4 {
class View;
class ViewManager;
+enum SceneTransition {
+ kTransitionNone = 0,
+ kTransitionFadeIn = 1,
+ kTransitionFadeIn2 = 2,
+ kTransitionBoxInBottomLeft = 3,
+ kTransitionBoxInBottomRight = 4,
+ kTransitionBoxInTopLeft = 5,
+ kTransitionBoxInTopRight = 6,
+ kTransitionPanLeftToRight = 7,
+ kTransitionPanRightToLeft = 8,
+ kTransitionCircleIn = 9
+};
+
enum {SCREEN_DIALOG, SCREEN_BUFFER, SCREEN_TEXT, SCREEN_TRANSPARENT};
enum ScreenEventType {SCREVENT_NONE = 0, SCREVENT_KEY = 1, SCREVENT_MOUSE = 2, SCREVENT_ALL = 3};
enum ScreenLayers {
diff --git a/engines/made/database.cpp b/engines/made/database.cpp
index ae1a08e1b5..51308cb7e5 100644
--- a/engines/made/database.cpp
+++ b/engines/made/database.cpp
@@ -515,6 +515,8 @@ int16 GameDatabaseV2::loadgame(const char *filename, int16 version) {
_objects[i]->load(*in);
}
delete in;
+
+ _objectPropertyCache.clear(); // make sure to clear cache
return result;
}
@@ -644,6 +646,8 @@ void GameDatabaseV3::load(Common::SeekableReadStream &sourceS) {
void GameDatabaseV3::reloadFromStream(Common::SeekableReadStream &sourceS) {
sourceS.seek(_gameStateOffs);
sourceS.read(_gameState, _gameStateSize);
+
+ _objectPropertyCache.clear(); // make sure to clear cache
}
bool GameDatabaseV3::getSavegameDescription(const char *filename, Common::String &description, int16 version) {
@@ -734,6 +738,9 @@ int16 GameDatabaseV3::loadgame(const char *filename, int16 version) {
in->skip(64); // skip savegame description
in->read(_gameState, _gameStateSize);
delete in;
+
+ _objectPropertyCache.clear(); // make sure to clear cache
+
return 0;
}
diff --git a/engines/made/detection.cpp b/engines/made/detection.cpp
index 1dfc0c3f83..6a6a70cb30 100644
--- a/engines/made/detection.cpp
+++ b/engines/made/detection.cpp
@@ -76,7 +76,6 @@ using Common::GUIO_NONE;
using Common::GUIO_NOSPEECH;
static const MadeGameDescription gameDescriptions[] = {
-
{
// NOTE: Return to Zork entries with *.dat are used to detect the game via rtzcd.dat,
// which is packed inside rtzcd.red. Entries with *.red refer to the packed file
@@ -329,6 +328,60 @@ static const MadeGameDescription gameDescriptions[] = {
},
{
+ // Return to Zork - Japanese DOS
+ // This is the RTZCD.DAT in the base directory of the FM-Towns CD
+ {
+ "rtz",
+ "",
+ AD_ENTRY1("rtzcd.dat", "c4fccf67ad247f09b94c3c808b138576"),
+ Common::JA_JPN,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_RTZ,
+ 0,
+ GF_CD,
+ 3,
+ },
+
+ {
+ // Return to Zork - Japanese FM-Towns
+ // This is in the RTZFM folder of the FM-Towns CD
+ {
+ "rtz",
+ "",
+ AD_ENTRY1("rtzcd.dat", "e949a6a42d82daabfa7d4dc0a87a9843"),
+ Common::JA_JPN,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_RTZ,
+ 0,
+ GF_CD,
+ 3,
+ },
+
+ {
+ // Return to Zork - Japanese PC-98
+ // This is in the RTZ9821 folder of the FM-Towns CD
+ {
+ "rtz",
+ "",
+ AD_ENTRY1("rtzcd.dat", "0c0117e98530c736a141c2aad6834dc5"),
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_RTZ,
+ 0,
+ GF_CD,
+ 3,
+ },
+
+ {
// The Manhole: New and Enhanced
{
"manhole",
@@ -493,7 +546,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class MadeMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/made/made.cpp b/engines/made/made.cpp
index 54e2189471..94926014d3 100644
--- a/engines/made/made.cpp
+++ b/engines/made/made.cpp
@@ -97,11 +97,11 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng
_script = new ScriptInterpreter(this);
- MidiDriverType 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::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ //bool adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver *driver = MidiDriver::createMidi(dev);
if (native_mt32)
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
diff --git a/engines/made/resource.cpp b/engines/made/resource.cpp
index 28d46cf4ec..cdcb49f9f9 100644
--- a/engines/made/resource.cpp
+++ b/engines/made/resource.cpp
@@ -50,10 +50,9 @@ PictureResource::~PictureResource() {
delete _picture;
_picture = 0;
}
- if (_picturePalette) {
- delete[] _picturePalette;
- _picturePalette = 0;
- }
+
+ delete[] _picturePalette;
+ _picturePalette = 0;
}
void PictureResource::load(byte *source, int size) {
diff --git a/engines/made/scriptfuncs.cpp b/engines/made/scriptfuncs.cpp
index cd4081ea52..8d01ec70f3 100644
--- a/engines/made/scriptfuncs.cpp
+++ b/engines/made/scriptfuncs.cpp
@@ -28,6 +28,7 @@
#include "common/events.h"
#include "graphics/cursorman.h"
#include "sound/audiocd.h"
+#include "sound/softsynth/pcspk.h"
#include "made/made.h"
#include "made/resource.h"
@@ -41,6 +42,22 @@
namespace Made {
+ScriptFunctions::ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false) {
+ // Initialize the two tone generators
+ _pcSpeaker1 = new Audio::PCSpeaker();
+ _pcSpeaker2 = new Audio::PCSpeaker();
+ _vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_pcSpeakerHandle1, _pcSpeaker1);
+ _vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_pcSpeakerHandle2, _pcSpeaker2);
+}
+
+ScriptFunctions::~ScriptFunctions() {
+ for (uint i = 0; i < _externalFuncs.size(); ++i)
+ delete _externalFuncs[i];
+
+ _vm->_system->getMixer()->stopHandle(_pcSpeakerHandle1);
+ _vm->_system->getMixer()->stopHandle(_pcSpeakerHandle2);
+}
+
typedef Common::Functor2Mem<int16, int16*, int16, ScriptFunctions> ExternalScriptFunc;
#define External(x) \
_externalFuncs.push_back(new ExternalScriptFunc(this, &ScriptFunctions::x)); \
@@ -308,36 +325,78 @@ int16 ScriptFunctions::sfFlashScreen(int16 argc, int16 *argv) {
}
int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) {
- // TODO: Used in Manhole:NE, Manhole EGA
- // This is used when using the piano in the desk screen inside the ship.
+ // This is used when using the piano in the desk screen inside the ship
+ // in The Manhole (EGA/NE).
+
// It takes 2 parameters:
- // The first parameter is the key pressed
+ // The first parameter is the note number of the key pressed + 1
// The second parameter is some sort of modifier (volume, perhaps?),
- // depending on which of the 3 keys on the right has been pressed (12 - 14)
- warning("Unimplemented opcode: sfPlayNote");
+ // depending on which of the 3 keys on the right has been pressed.
+ // This value seems to be [12, 14] in NE and [1, 3] in EGA.
+
+ // Note frequencies based on http://www.phy.mtu.edu/~suits/notefreqs.html
+ static const int freqTable[] = {
+ 16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29,
+ 30, 32, 35, 37, 39, 41, 44, 46, 49, 52, 55,
+ 58, 62, 65, 69, 73, 77, 82, 87, 93, 98, 104,
+ 110, 117, 123, 131, 139, 147, 156, 165, 175, 195,
+ 196, 208, 220, 233, 247, 262, 277, 294, 311, 330,
+ 349, 370, 392, 415, 440, 466, 494, 523, 554, 587,
+ 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047,
+ 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760,
+ 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960,
+ 3136, 3322, 3529, 3729, 3951, 4186, 4435, 4697, 4978
+ };
+
+ debug(4, "sfPlayNote: Note = %d, Volume(?) = %d", argv[0] - 1, argv[1]);
+
+ _pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable[argv[0] - 1], -1);
+
+ // TODO: Figure out what to do with the second parameter
+ //_pcSpeaker1->setVolume(argv[1]);
+
return 0;
}
int16 ScriptFunctions::sfStopNote(int16 argc, int16 *argv) {
- // TODO: Used in Manhole:NE, Manhole EGA
// Used in the same place as sfPlayNote, with the same parameters
- warning("Unimplemented opcode: sfStopNote");
+ // We just stop the wave generator here
+ _pcSpeaker1->stop();
return 0;
}
int16 ScriptFunctions::sfPlayTele(int16 argc, int16 *argv) {
- // TODO: Used in Manhole:NE, Manhole EGA
// This is used when pressing the phone keys while using the phone in
- // the desk screen inside the ship.
+ // the desk screen inside the ship in The Manhole (EGA/NE).
// It takes 1 parameter, the key pressed (0-9, 10 for asterisk, 11 for hash)
- warning("Unimplemented opcode: sfPlayTele");
+
+ // A telephone keypad uses a two tones for each key.
+ // See http://en.wikipedia.org/wiki/Telephone_keypad for more info
+
+ static const int freqTable1[] = {
+ 1336, 1209, 1336, 1477,
+ 1209, 1336, 1477, 1209,
+ 1336, 1477, 1209, 1477
+ };
+
+ static const int freqTable2[] = {
+ 941, 697, 697, 697,
+ 770, 770, 770, 852,
+ 852, 852, 941, 941
+ };
+
+ debug(4, "sfPlayTele: Button = %d", argv[0]);
+
+ _pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable1[argv[0]], -1);
+ _pcSpeaker2->play(Audio::PCSpeaker::kWaveFormSine, freqTable2[argv[0]], -1);
return 0;
}
int16 ScriptFunctions::sfStopTele(int16 argc, int16 *argv) {
- // TODO: Used in Manhole:NE, Manhole EGA
// Used in the same place as sfPlayTele, with the same parameters
- warning("Unimplemented opcode: sfStopTele");
+ // We just stop both wave generators here
+ _pcSpeaker1->stop();
+ _pcSpeaker2->stop();
return 0;
}
diff --git a/engines/made/scriptfuncs.h b/engines/made/scriptfuncs.h
index 5fdfb77f45..3bed27c5c8 100644
--- a/engines/made/scriptfuncs.h
+++ b/engines/made/scriptfuncs.h
@@ -33,6 +33,10 @@
#include "made/resource.h"
+namespace Audio {
+ class PCSpeaker;
+}
+
namespace Made {
class MadeEngine;
@@ -41,17 +45,16 @@ typedef Common::Functor2<int16, int16*, int16> ExternalFunc;
class ScriptFunctions {
public:
- ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false) {}
- virtual ~ScriptFunctions() {
- for (uint i = 0; i < _externalFuncs.size(); ++i)
- delete _externalFuncs[i];
- }
+ ScriptFunctions(MadeEngine *vm);
+ virtual ~ScriptFunctions();
+
int16 callFunction(uint16 index, int16 argc, int16 *argv) {
if (index >= _externalFuncs.size())
error("ScriptFunctions::callFunction() Invalid function index %d", index);
debug(4, "%s", _externalFuncNames[index]);
return (*_externalFuncs[index])(argc, argv);
}
+
void setupExternalsTable();
const char* getFuncName(int index) { return _externalFuncNames[index]; }
int getCount() const { return _externalFuncs.size(); }
@@ -64,6 +67,10 @@ protected:
SoundResource* _soundResource;
bool _soundStarted;
+ // PlayNote/StopNote and PlayTele/StopTele wave generators
+ Audio::SoundHandle _pcSpeakerHandle1, _pcSpeakerHandle2;
+ Audio::PCSpeaker *_pcSpeaker1, *_pcSpeaker2;
+
Common::Array<const ExternalFunc*> _externalFuncs;
Common::Array<const char *> _externalFuncNames;
GenericResource *_musicRes;
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 389665db39..5dcfff4f90 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -28,9 +28,10 @@
#include "mohawk/myst_scripts.h"
#include "mohawk/graphics.h"
#include "mohawk/riven.h"
+#include "mohawk/riven_external.h"
#include "mohawk/livingbooks.h"
#include "mohawk/sound.h"
-#include "mohawk/video/video.h"
+#include "mohawk/video.h"
namespace Mohawk {
@@ -307,6 +308,7 @@ RivenConsole::RivenConsole(MohawkEngine_Riven *vm) : GUI::Debugger(), _vm(vm) {
DCmd_Register("dumpScript", WRAP_METHOD(RivenConsole, Cmd_DumpScript));
DCmd_Register("listZipCards", WRAP_METHOD(RivenConsole, Cmd_ListZipCards));
DCmd_Register("getRMAP", WRAP_METHOD(RivenConsole, Cmd_GetRMAP));
+ DCmd_Register("combos", WRAP_METHOD(RivenConsole, Cmd_Combos));
}
RivenConsole::~RivenConsole() {
@@ -556,9 +558,11 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
printf ("==================================\n\n");
Common::SeekableReadStream *cardStream = _vm->getRawData(MKID_BE('CARD'), (uint16)atoi(argv[3]));
cardStream->seek(4);
- RivenScriptList scriptList = RivenScript::readScripts(_vm, cardStream);
- for (uint32 i = 0; i < scriptList.size(); i++)
+ RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream, false);
+ for (uint32 i = 0; i < scriptList.size(); i++) {
scriptList[i]->dumpScript(varNames, xNames, 0);
+ delete scriptList[i];
+ }
delete cardStream;
} else if (!scumm_stricmp(argv[2], "HSPT")) {
printf ("\n\nDumping scripts for %s\'s card %d hotspots!\n", argv[1], (uint16)atoi(argv[3]));
@@ -571,9 +575,11 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
for (uint16 i = 0; i < hotspotCount; i++) {
printf ("Hotspot %d:\n", i);
hsptStream->seek(22, SEEK_CUR); // Skip non-script related stuff
- RivenScriptList scriptList = RivenScript::readScripts(_vm, hsptStream);
- for (uint32 j = 0; j < scriptList.size(); j++)
+ RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream, false);
+ for (uint32 j = 0; j < scriptList.size(); j++) {
scriptList[j]->dumpScript(varNames, xNames, 1);
+ delete scriptList[j];
+ }
}
delete hsptStream;
@@ -608,6 +614,33 @@ bool RivenConsole::Cmd_GetRMAP(int argc, const char **argv) {
return true;
}
+bool RivenConsole::Cmd_Combos(int argc, const char **argv) {
+ // In the vain of SCUMM's 'drafts' command, this command will list
+ // out all combinations needed in Riven, decoded from the variables.
+ // You'll need to look up the Rebel Tunnel puzzle on your own; the
+ // solution is constant.
+
+ uint32 teleCombo = *_vm->matchVarToString("tcorrectorder");
+ uint32 prisonCombo = *_vm->matchVarToString("pcorrectorder");
+ uint32 domeCombo = *_vm->matchVarToString("adomecombo");
+
+ DebugPrintf("Telescope Combo:\n ");
+ for (int i = 0; i < 5; i++)
+ DebugPrintf("%d ", _vm->_externalScriptHandler->getComboDigit(teleCombo, i));
+
+ DebugPrintf("\nPrison Combo:\n ");
+ for (int i = 0; i < 5; i++)
+ DebugPrintf("%d ", _vm->_externalScriptHandler->getComboDigit(prisonCombo, i));
+
+ DebugPrintf("\nDome Combo:\n ");
+ for (int i = 1; i <= 25; i++)
+ if (domeCombo & (1 << (25 - i)))
+ DebugPrintf("%d ", i);
+
+ DebugPrintf("\n");
+ return true;
+}
+
LivingBooksConsole::LivingBooksConsole(MohawkEngine_LivingBooks *vm) : GUI::Debugger(), _vm(vm) {
DCmd_Register("playSound", WRAP_METHOD(LivingBooksConsole, Cmd_PlaySound));
DCmd_Register("stopSound", WRAP_METHOD(LivingBooksConsole, Cmd_StopSound));
diff --git a/engines/mohawk/console.h b/engines/mohawk/console.h
index 9a30d46225..1806c61027 100644
--- a/engines/mohawk/console.h
+++ b/engines/mohawk/console.h
@@ -88,6 +88,7 @@ private:
bool Cmd_DumpScript(int argc, const char **argv);
bool Cmd_ListZipCards(int argc, const char **argv);
bool Cmd_GetRMAP(int argc, const char **argv);
+ bool Cmd_Combos(int argc, const char **argv);
};
class LivingBooksConsole : public GUI::Debugger {
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index a7b1fe7fae..f04338239f 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -117,846 +117,21 @@ static const PlainGameDescriptor mohawkGames[] = {
{"ruff", "Ruff's Bone"},
{"newkid", "The New Kid on the Block"},
{"arthurrace", "Arthur's Reading Race"},
+ {"arthurbday", "Arthur's Birthday"},
+ {"lilmonster", "Little Monster at School"},
#endif
{0, 0}
};
+#include "mohawk/detection_tables.h"
-namespace Mohawk {
-
-static const MohawkGameDescription gameDescriptions[] = {
- // Myst
- // English Windows 3.11
- // From clone2727
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "ae3258c9c90128d274aa6a790b3ad181"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst Demo
- // English Windows 3.11
- // From CD-ROM Today July, 1994
- {
- {
- "myst",
- "Demo",
- AD_ENTRY1("DEMO.DAT", "c39303dd53fb5c4e7f3c23231c606cd0"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_DEMO,
- 0,
- },
-
- // Myst
- // German Windows 3.11
- // From clone2727
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "4beb3366ed3f3b9bfb6e81a14a43bdcc"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // German Windows 3.11
- // From LordHoto
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "e0937cca1ab125e48e30dc3cd5046ddf"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // Spanish Windows ?
- // From jvprat
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "f7e7d7ca69934f1351b5acd4fe4d44c2"),
- Common::ES_ESP,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // Japanese Windows 3.11
- // From clone2727
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "032c88e3b7e8db4ca475e7b7db9a66bb"),
- Common::JA_JPN,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // French Windows 3.11
- // From Strangerke
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "d631d42567a941c67c78f2e491f4ea58"),
- Common::FR_FRA,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Making of Myst
- // English Windows 3.11
- // From clone2727
- {
- {
- "MakingOfMyst",
- "",
- AD_ENTRY1("MAKING.DAT", "f6387e8f0f7b8a3e42c95294315d6a0e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAKINGOF,
- 0,
- 0,
- },
-
- // Making of Myst
- // Japanese Windows 3.11
- // From clone2727
- {
- {
- "MakingOfMyst",
- "",
- AD_ENTRY1("MAKING.DAT", "03ff62607e64419ab2b6ebf7b7bcdf63"),
- Common::JA_JPN,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAKINGOF,
- 0,
- 0,
- },
-
- // Myst Masterpiece Edition
- // English Windows
- // From clone2727
- {
- {
- "myst",
- "Masterpiece Edition",
- AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 0,
- },
-
- // Myst Masterpiece Edition
- // English Windows
- // From clone2727
- {
- {
- "myst",
- "Masterpiece Edition",
- AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 0,
- },
-
- // Myst Masterpiece Edition
- // German Windows
- // From DrMcCoy (Included in "Myst: Die Trilogie")
- {
- {
- "myst",
- "Masterpiece Edition",
- AD_ENTRY1("MYST.DAT", "f88e0ace66dbca78eebdaaa1d3314ceb"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.0 (5CD)
- // From clone2727
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "71145fdecbd68a0cfc292c2fbddf8e08"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.03 (5CD)
- // From ST
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "d8ccae34a0e3c709135a73f449b783be"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.? (5CD)
- // From jvprat
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "249e8c995d191b03ee94c892c0eac775"),
- Common::ES_ESP,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.? (DVD, From "Myst 10th Anniversary Edition")
- // From Clone2727
- {
- {
- "riven",
- "DVD",
- AD_ENTRY1("a_Data.MHK", "08fcaa5d5a2a01d7a5a6960f497212fe"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DVD,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.0 (DVD, From "Myst: Die Trilogie")
- // From DrMcCoy
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "a5fe1c91a6033eb6ee54b287578b74b9"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DVD,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version ? (Demo, From "Prince of Persia Collector's Edition")
- // From Clone2727
- {
- {
- "riven",
- "Demo",
- AD_ENTRY1("a_Data.MHK", "bae6b03bd8d6eb350d35fd13f0e3139f"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DEMO,
- 0,
- },
-
-#ifdef DETECT_BRODERBUND_TITLES
- {
- {
- "zoombini",
- "",
- AD_ENTRY1("ZOOMBINI.MHK", "98b758fec55104c096cfd129048be9a6"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_ZOOMBINI,
- GF_HASMIDI,
- 0
- },
-
- {
- {
- "csworld",
- "v3.0",
- AD_ENTRY1("C2K.MHK", "605fe88380848031bbd0ff84ade6fe40"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSWORLD,
- 0,
- 0
- },
-
- {
- {
- "csworld",
- "v3.5",
- AD_ENTRY1("C2K.MHK", "d4857aeb0f5e2e0c4ac556aa74f38c23"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSWORLD,
- 0,
- 0
- },
-
- {
- {
- "csamtrak",
- "",
- AD_ENTRY1("AMTRAK.MHK", "2f95301f0bb950d555bb7b0e3b1b7eb1"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSAMTRAK,
- 0,
- 0
- },
-
- {
- {
- "maggiess",
- "",
- AD_ENTRY1("MAGGIESS.MHK", "08f75fc8c0390e68fdada5ddb35d0355"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAGGIESS,
- 0,
- 0
- },
-
- {
- {
- "jamesmath",
- "",
- AD_ENTRY1("BRODER.MHK", "007299da8b2c6e8ec1cde9598c243024"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_JAMESMATH,
- GF_HASMIDI,
- 0
- },
-
- // This is in the NEWDATA folder, so I assume it's a newer version ;)
- {
- {
- "jamesmath",
- "",
- AD_ENTRY1("BRODER.MHK", "53c000938a50dca92860fd9b546dd276"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_JAMESMATH,
- GF_HASMIDI,
- 1
- },
-
- {
- {
- "treehouse",
- "",
- AD_ENTRY1("MAINROOM.MHK", "12f51894d7f838af639ea9bf1bc8f45b"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_TREEHOUSE,
- GF_HASMIDI,
- 0
- },
-
- {
- {
- "greeneggs",
- "",
- AD_ENTRY1("GREEN.LB", "5df8438138186f89e71299d7b4f88d06"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-
- // 32-bit version of the previous entry
- {
- {
- "greeneggs",
- "",
- AD_ENTRY1("GREEN32.LB", "5df8438138186f89e71299d7b4f88d06"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-
- {
- {
- "1stdegree",
- "",
- AD_ENTRY1("AL236_1.MHK", "3ba145492a7b8b4dee0ef4222c5639c3"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_1STDEGREE,
- GF_HASMIDI,
- 0
- },
-
- // In The 1st Degree
- // French Windows
- // From Strangerke
- {
- {
- "1stdegree",
- "",
- AD_ENTRY1("AL236_1.MHK", "0e0c70b1b702b6ddca61a1192ada1282"),
- Common::FR_FRA,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_1STDEGREE,
- GF_HASMIDI,
- 0
- },
-
- {
- {
- "csusa",
- "",
- AD_ENTRY1("USAC2K.MHK", "b8c9d3a2586f62bce3a48b50d7a700e9"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSUSA,
- 0,
- 0
- },
-
- {
- {
- "tortoise",
- "Demo v1.0",
- AD_ENTRY1("TORTOISE.512", "75d9a2f8339e423604a0c6e8177600a6"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "tortoise",
- "Demo v1.1",
- AD_ENTRY1("TORTOISE.512", "a38c99360e2bea3bfdec418469aef022"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "arthur",
- "",
- AD_ENTRY1("PAGES.512", "1550a361454ec452fe7d2328aac2003c"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- 0,
- 0
- },
-
- {
- {
- "arthur",
- "Demo",
- AD_ENTRY1("PAGES.512", "a4d68cef197af1416921ca5b2e0c1e31"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "arthur",
- "Demo",
- AD_ENTRY1("Bookoutline", "7e2691611ff4c7b89c05221736628059"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "grandma",
- "Demo v1.0",
- AD_ENTRY1("PAGES.512", "95d9f4b035bf5d15c57a9189f231b0f8"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "grandma",
- "Demo v1.1",
- AD_ENTRY1("GRANDMA.512", "72a4d5fb1b3f06b5f75425635d42ce2e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "grandma",
- "Demo",
- AD_ENTRY1("Bookoutline", "553c93891b9631d1e1d269599e1efa6c"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "ruff",
- "Demo",
- AD_ENTRY1("RUFF.512", "2ba1aa65177c816e156db648c398d362"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "ruff",
- "Demo",
- AD_ENTRY1("Ruff's Bone Demo", "22553ac2ceb2a166bdf1def6ad348532"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "newkid",
- "Demo v1.0",
- AD_ENTRY1("NEWKID.512", "2b9d94763a50d514c04a3af488934f73"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "newkid",
- "Demo v1.1",
- AD_ENTRY1("NEWKID.512", "41e975b7390c626f8d1058a34f9d9b2e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "arthurrace",
- "",
- AD_ENTRY1("RACE.LB", "1645f36bcb36e440d928e920aa48c373"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-
- // 32-bit version of the previous entry
- {
- {
- "arthurrace",
- "",
- AD_ENTRY1("RACE32.LB", "292a05bc48c1dd9583821a4181a02ef2"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-#endif
-
- { AD_TABLE_END_MARKER, 0, 0, 0 }
+static const char *directoryGlobs[] = {
+ "all",
+ "assets1",
+ "data",
+ 0
};
-//////////////////////////////
-//Fallback detection
-//////////////////////////////
-
-static const MohawkGameDescription fallbackDescs[] = {
- {
- {
- "myst",
- "unknown",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0
- },
-
- {
- {
- "MakingOfMyst",
- "unknown",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAKINGOF,
- 0,
- 0
- },
-
- {
- {
- "myst",
- "unknown (Masterpiece Edition)",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 0
- },
-
- {
- {
- "riven",
- "unknown",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0
- },
-
- {
- {
- "riven",
- "unknown (DVD)",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DVD,
- 0
- }
-};
-
-static const ADFileBasedFallback fileBased[] = {
- { &fallbackDescs[0], { "MYST.DAT", 0 } },
- { &fallbackDescs[1], { "MAKING.DAT", 0 } },
- { &fallbackDescs[2], { "MYST.DAT", "Help.dat", 0 } }, // Help system doesn't exist in original
- { &fallbackDescs[3], { "a_Data.MHK", 0 } },
- { &fallbackDescs[4], { "a_Data.MHK", "t_Data1.MHK" , 0 } },
- { 0, { 0 } }
-};
-
-} // End of namespace Mohawk
-
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)Mohawk::gameDescriptions,
@@ -975,7 +150,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game)
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 2,
+ // List of directory globs
+ directoryGlobs
};
class MohawkMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
new file mode 100644
index 0000000000..7470dbf1dd
--- /dev/null
+++ b/engines/mohawk/detection_tables.h
@@ -0,0 +1,1030 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+namespace Mohawk {
+
+static const MohawkGameDescription gameDescriptions[] = {
+ // Myst
+ // English Windows 3.11
+ // From clone2727
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "ae3258c9c90128d274aa6a790b3ad181"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst Demo
+ // English Windows 3.11
+ // From CD-ROM Today July, 1994
+ {
+ {
+ "myst",
+ "Demo",
+ AD_ENTRY1("DEMO.DAT", "c39303dd53fb5c4e7f3c23231c606cd0"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_DEMO,
+ 0,
+ },
+
+ // Myst
+ // German Windows 3.11
+ // From clone2727
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "4beb3366ed3f3b9bfb6e81a14a43bdcc"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // German Windows 3.11
+ // From LordHoto
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "e0937cca1ab125e48e30dc3cd5046ddf"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // Spanish Windows ?
+ // From jvprat
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "f7e7d7ca69934f1351b5acd4fe4d44c2"),
+ Common::ES_ESP,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // Japanese Windows 3.11
+ // From clone2727
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "032c88e3b7e8db4ca475e7b7db9a66bb"),
+ Common::JA_JPN,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // French Windows 3.11
+ // From Strangerke
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "d631d42567a941c67c78f2e491f4ea58"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Making of Myst
+ // English Windows 3.11
+ // From clone2727
+ {
+ {
+ "MakingOfMyst",
+ "",
+ AD_ENTRY1("MAKING.DAT", "f6387e8f0f7b8a3e42c95294315d6a0e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAKINGOF,
+ 0,
+ 0,
+ },
+
+ // Making of Myst
+ // Japanese Windows 3.11
+ // From clone2727
+ {
+ {
+ "MakingOfMyst",
+ "",
+ AD_ENTRY1("MAKING.DAT", "03ff62607e64419ab2b6ebf7b7bcdf63"),
+ Common::JA_JPN,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAKINGOF,
+ 0,
+ 0,
+ },
+
+ // Myst Masterpiece Edition
+ // English Windows
+ // From clone2727
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0,
+ },
+
+ // Myst Masterpiece Edition
+ // English Windows
+ // From clone2727
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0,
+ },
+
+ // Myst Masterpiece Edition
+ // German Windows
+ // From DrMcCoy (Included in "Myst: Die Trilogie")
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "f88e0ace66dbca78eebdaaa1d3314ceb"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0,
+ },
+
+ // Myst Masterpiece Edition
+ // French Windows
+ // From gamin (Included in "Myst: La Trilogie")
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "aea81633b2d2ae498f09072fb87263b6"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.0 (5CD)
+ // From clone2727
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "71145fdecbd68a0cfc292c2fbddf8e08"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.03 (5CD)
+ // From ST
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "d8ccae34a0e3c709135a73f449b783be"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.? (5CD)
+ // From jvprat
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "249e8c995d191b03ee94c892c0eac775"),
+ Common::ES_ESP,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.? (DVD, From "Myst 10th Anniversary Edition")
+ // From Clone2727
+ {
+ {
+ "riven",
+ "DVD",
+ AD_ENTRY1("a_Data.MHK", "08fcaa5d5a2a01d7a5a6960f497212fe"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.0 (DVD, From "Myst: Die Trilogie")
+ // From DrMcCoy
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "a5fe1c91a6033eb6ee54b287578b74b9"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version ? (DVD, From "Myst: La Trilogie")
+ // From gamin
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version ? (Demo, From "Prince of Persia Collector's Edition")
+ // From Clone2727
+ {
+ {
+ "riven",
+ "Demo",
+ AD_ENTRY1("a_Data.MHK", "bae6b03bd8d6eb350d35fd13f0e3139f"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DEMO,
+ 0,
+ },
+
+#ifdef DETECT_BRODERBUND_TITLES
+ {
+ {
+ "zoombini",
+ "",
+ AD_ENTRY1("ZOOMBINI.MHK", "98b758fec55104c096cfd129048be9a6"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_ZOOMBINI,
+ GF_HASMIDI,
+ 0
+ },
+
+ {
+ {
+ "csworld",
+ "v3.0",
+ AD_ENTRY1("C2K.MHK", "605fe88380848031bbd0ff84ade6fe40"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSWORLD,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "csworld",
+ "v3.5",
+ AD_ENTRY1("C2K.MHK", "d4857aeb0f5e2e0c4ac556aa74f38c23"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSWORLD,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "csamtrak",
+ "",
+ AD_ENTRY1("AMTRAK.MHK", "2f95301f0bb950d555bb7b0e3b1b7eb1"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSAMTRAK,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "maggiess",
+ "",
+ AD_ENTRY1("MAGGIESS.MHK", "08f75fc8c0390e68fdada5ddb35d0355"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAGGIESS,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "jamesmath",
+ "",
+ AD_ENTRY1("BRODER.MHK", "007299da8b2c6e8ec1cde9598c243024"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_JAMESMATH,
+ GF_HASMIDI,
+ 0
+ },
+
+ // This is in the NEWDATA folder, so I assume it's a newer version ;)
+ {
+ {
+ "jamesmath",
+ "",
+ AD_ENTRY1("BRODER.MHK", "53c000938a50dca92860fd9b546dd276"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_JAMESMATH,
+ GF_HASMIDI,
+ 1
+ },
+
+ {
+ {
+ "treehouse",
+ "",
+ AD_ENTRY1("MAINROOM.MHK", "12f51894d7f838af639ea9bf1bc8f45b"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_TREEHOUSE,
+ GF_HASMIDI,
+ 0
+ },
+
+ {
+ {
+ "greeneggs",
+ "",
+ AD_ENTRY1("GREEN.LB", "5df8438138186f89e71299d7b4f88d06"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ // 32-bit version of the previous entry
+ {
+ {
+ "greeneggs",
+ "",
+ AD_ENTRY1("GREEN32.LB", "5df8438138186f89e71299d7b4f88d06"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "1stdegree",
+ "",
+ AD_ENTRY1("AL236_1.MHK", "3ba145492a7b8b4dee0ef4222c5639c3"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_1STDEGREE,
+ GF_HASMIDI,
+ 0
+ },
+
+ // In The 1st Degree
+ // French Windows
+ // From Strangerke
+ {
+ {
+ "1stdegree",
+ "",
+ AD_ENTRY1("AL236_1.MHK", "0e0c70b1b702b6ddca61a1192ada1282"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_1STDEGREE,
+ GF_HASMIDI,
+ 0
+ },
+
+ {
+ {
+ "csusa",
+ "",
+ AD_ENTRY1("USAC2K.MHK", "b8c9d3a2586f62bce3a48b50d7a700e9"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSUSA,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "tortoise",
+ "Demo v1.0",
+ AD_ENTRY1("TORTOISE.512", "75d9a2f8339e423604a0c6e8177600a6"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "tortoise",
+ "Demo v1.1",
+ AD_ENTRY1("TORTOISE.512", "a38c99360e2bea3bfdec418469aef022"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "tortoise",
+ "Demo",
+ AD_ENTRY1("The Tortoise and the Hare Demo", "35d571806838667743c7c15a133e9335"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "",
+ AD_ENTRY1("PAGES.512", "1550a361454ec452fe7d2328aac2003c"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo",
+ AD_ENTRY1("PAGES.512", "a4d68cef197af1416921ca5b2e0c1e31"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo v1.1",
+ AD_ENTRY1("ARTHUR.512", "f19e824e0a2f2745ed698e6aaf44f838"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo",
+ AD_ENTRY1("Bookoutline", "7e2691611ff4c7b89c05221736628059"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo",
+ AD_ENTRY1("Arthur's Teacher Trouble Demo", "dcbd8af6bf25854df8ad36fd13665d08"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo v1.0",
+ AD_ENTRY1("PAGES.512", "95d9f4b035bf5d15c57a9189f231b0f8"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo v1.1",
+ AD_ENTRY1("GRANDMA.512", "72a4d5fb1b3f06b5f75425635d42ce2e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo",
+ AD_ENTRY1("Bookoutline", "553c93891b9631d1e1d269599e1efa6c"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo",
+ AD_ENTRY1("Just Grandma and Me Demo", "552d8729fa77a4a83c88283c7d79bd31"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "ruff",
+ "Demo",
+ AD_ENTRY1("RUFF.512", "2ba1aa65177c816e156db648c398d362"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "ruff",
+ "Demo",
+ AD_ENTRY1("Ruff's Bone Demo", "22553ac2ceb2a166bdf1def6ad348532"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "newkid",
+ "Demo v1.0",
+ AD_ENTRY1("NEWKID.512", "2b9d94763a50d514c04a3af488934f73"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "newkid",
+ "Demo v1.1",
+ AD_ENTRY1("NEWKID.512", "41e975b7390c626f8d1058a34f9d9b2e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "newkid",
+ "Demo",
+ AD_ENTRY1("The New Kid on the Block Demo", "7d33237e0ea452a97f2a3acdfb9e1286"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthurrace",
+ "",
+ AD_ENTRY1("RACE.LB", "1645f36bcb36e440d928e920aa48c373"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ // 32-bit version of the previous entry
+ {
+ {
+ "arthurrace",
+ "",
+ AD_ENTRY1("RACE32.LB", "292a05bc48c1dd9583821a4181a02ef2"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "arthurbday",
+ "Demo",
+ AD_ENTRY1("BIRTHDAY.512", "fb73e387cfec65c5c930db068a8f468a"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthurbday",
+ "Demo",
+ AD_ENTRY1("Arthur's Birthday Demo", "0d974ec635eea615475368e865f1b1c8"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "lilmonster",
+ "",
+ AD_ENTRY1("MONSTER.512", "e7b24bf8f59106b5c4df51b39eb8c0ef"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "lilmonster",
+ "",
+ AD_ENTRY1("BookOutline", "970409f9d967d63c05e63113f8e78fe2"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ 0,
+ 0
+ },
+#endif
+
+ { AD_TABLE_END_MARKER, 0, 0, 0 }
+};
+
+//////////////////////////////
+//Fallback detection
+//////////////////////////////
+
+static const MohawkGameDescription fallbackDescs[] = {
+ {
+ {
+ "myst",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "MakingOfMyst",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAKINGOF,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "myst",
+ "unknown (Masterpiece Edition)",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0
+ },
+
+ {
+ {
+ "riven",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "riven",
+ "unknown (DVD)",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0
+ }
+};
+
+static const ADFileBasedFallback fileBased[] = {
+ { &fallbackDescs[0], { "MYST.DAT", 0 } },
+ { &fallbackDescs[1], { "MAKING.DAT", 0 } },
+ { &fallbackDescs[2], { "MYST.DAT", "Help.dat", 0 } }, // Help system doesn't exist in original
+ { &fallbackDescs[3], { "a_Data.MHK", 0 } },
+ { &fallbackDescs[4], { "a_Data.MHK", "t_Data1.MHK" , 0 } },
+ { 0, { 0 } }
+};
+
+} // End of Namespace Mohawk
diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index c5327dbeea..c09763cbb3 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -30,6 +30,7 @@
#include "gui/GuiManager.h"
#include "common/savefile.h"
+#include "common/translation.h"
namespace Mohawk {
@@ -77,11 +78,11 @@ enum {
};
MystOptionsDialog::MystOptionsDialog(MohawkEngine_Myst* vm) : GUI::OptionsDialog("", 120, 120, 360, 200), _vm(vm) {
- _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, "Zip Mode Activated", kZipCmd, 'Z');
- _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, "Transitions Enabled", kTransCmd, 'T');
+ _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, _("~Z~ip Mode Activated"), 0, kZipCmd);
+ _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, _("~T~ransitions Enabled"), 0, kTransCmd);
- new GUI::ButtonWidget(this, 95, 160, 120, 25, "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, 225, 160, 120, 25, "Cancel", GUI::kCloseCmd, 'C');
+ new GUI::ButtonWidget(this, 95, 160, 120, 25, _("~O~K"), 0, GUI::kOKCmd);
+ new GUI::ButtonWidget(this, 225, 160, 120, 25, _("~C~ancel"), 0, GUI::kCloseCmd);
}
MystOptionsDialog::~MystOptionsDialog() {
@@ -111,11 +112,11 @@ void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, ui
}
RivenOptionsDialog::RivenOptionsDialog(MohawkEngine_Riven* vm) : GUI::OptionsDialog("", 120, 120, 360, 200), _vm(vm) {
- _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, "Zip Mode Activated", kZipCmd, 'Z');
- _waterEffectCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, "Water Effect Enabled", kWaterCmd, 'W');
+ _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, _("~Z~ip Mode Activated"), 0, kZipCmd);
+ _waterEffectCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, _("~W~ater Effect Enabled"), 0, kWaterCmd);
- new GUI::ButtonWidget(this, 95, 160, 120, 25, "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, 225, 160, 120, 25, "Cancel", GUI::kCloseCmd, 'C');
+ new GUI::ButtonWidget(this, 95, 160, 120, 25, _("~O~K"), 0, GUI::kOKCmd);
+ new GUI::ButtonWidget(this, 225, 160, 120, 25, _("~C~ancel"), 0, GUI::kCloseCmd);
}
RivenOptionsDialog::~RivenOptionsDialog() {
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
index 35691c36aa..1974aec9c2 100644
--- a/engines/mohawk/graphics.cpp
+++ b/engines/mohawk/graphics.cpp
@@ -78,9 +78,8 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : _vm(vm) {
error("Myst requires greater than 256 colors to run");
if (_vm->getFeatures() & GF_ME) {
- // We want to delete our own JPEG surfaces, so don't free after use.
- _jpegDecoder = new JPEGDecoder(false);
- _pictDecoder = new MystPICT(_jpegDecoder);
+ _jpegDecoder = new Graphics::JPEGDecoder();
+ _pictDecoder = new Graphics::PictDecoder(_pixelFormat);
} else {
_jpegDecoder = NULL;
_pictDecoder = NULL;
@@ -152,9 +151,10 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm
if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) {
for (uint32 i = 0; i < _pictureFile.pictureCount; i++)
if (_pictureFile.entries[i].id == image) {
- if (_pictureFile.entries[i].type == 0)
- surface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size));
- else if (_pictureFile.entries[i].type == 1)
+ if (_pictureFile.entries[i].type == 0) {
+ Graphics::Surface *jpegSurface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size));
+ surface->copyFrom(*jpegSurface);
+ } else if (_pictureFile.entries[i].type == 1)
surface = _pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size));
else
error ("Unknown Picture File type %d", _pictureFile.entries[i].type);
@@ -466,27 +466,22 @@ void RivenGraphics::runScheduledTransition() {
// transitions were found by hacking scripts.
switch (_scheduledTransition) {
+ case 0: // Swipe Left
+ case 1: // Swipe Right
+ case 2: // Swipe Up
+ case 3: // Swipe Down
case 12: // Pan Left
- warning ("STUB: Pan left");
- break;
case 13: // Pan Right
- warning ("STUB: Pan right");
- break;
case 14: // Pan Up
- warning ("STUB: Pan up");
- break;
case 15: // Pan Down
- warning ("STUB: Pan down");
- break;
case 16: // Dissolve
case 17: // Dissolve (tspit CARD 155)
- warning ("STUB: Dissolve");
break;
default:
- if (_scheduledTransition < 12)
- error ("Found unused transition %d", _scheduledTransition);
+ if (_scheduledTransition >= 4 && _scheduledTransition <= 11)
+ error("Found unused transition %d", _scheduledTransition);
else
- error ("Found unknown transition %d", _scheduledTransition);
+ error("Found unknown transition %d", _scheduledTransition);
}
// For now, just copy the image to screen without doing any transition.
@@ -607,19 +602,23 @@ void RivenGraphics::showInventory() {
if (_vm->getFeatures() & GF_DEMO || _vm->getCurStack() == aspit)
return;
- // There are three books and three vars. However, there's only
- // a possible two combinations. Either you have only Atrus'
- // journal or you have all three books.
- // bool hasAtrusBook = *_vm->matchVarToString("aatrusbook") != 0;
+ // There are three books and three vars. We have three different
+ // combinations. At the start you have just Atrus' journal. Later,
+ // you get Catherine's journal and the trap book. Near the end,
+ // you lose the trap book and have just the two journals.
+
bool hasCathBook = *_vm->matchVarToString("acathbook") != 0;
- // bool hasTrapBook = *_vm->matchVarToString("atrapbook") != 0;
+ bool hasTrapBook = *_vm->matchVarToString("atrapbook") != 0;
if (!hasCathBook) {
- drawInventoryImage(101, g_atrusJournalRectSolo);
+ drawInventoryImage(101, g_atrusJournalRect1);
+ } else if (!hasTrapBook) {
+ drawInventoryImage(101, g_atrusJournalRect2);
+ drawInventoryImage(102, g_cathJournalRect2);
} else {
- drawInventoryImage(101, g_atrusJournalRect);
- drawInventoryImage(102, g_cathJournalRect);
- drawInventoryImage(100, g_trapBookRect);
+ drawInventoryImage(101, g_atrusJournalRect3);
+ drawInventoryImage(102, g_cathJournalRect3);
+ drawInventoryImage(100, g_trapBookRect3);
}
_vm->_system->updateScreen();
diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h
index 8d28e1ff4b..dd1764e6d6 100644
--- a/engines/mohawk/graphics.h
+++ b/engines/mohawk/graphics.h
@@ -27,11 +27,11 @@
#define MOHAWK_GRAPHICS_H
#include "mohawk/bitmap.h"
-#include "mohawk/jpeg.h"
#include "mohawk/livingbooks.h"
-#include "mohawk/myst_pict.h"
#include "common/file.h"
+#include "graphics/pict.h"
+#include "graphics/video/codecs/mjpeg.h"
namespace Mohawk {
@@ -96,16 +96,16 @@ public:
void loadExternalPictureFile(uint16 stack);
void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest);
void copyImageToScreen(uint16 image, Common::Rect dest);
- void showCursor(void);
- void hideCursor(void);
+ void showCursor();
+ void hideCursor();
void changeCursor(uint16);
void drawRect(Common::Rect rect, bool active);
private:
MohawkEngine_Myst *_vm;
MystBitmap *_bmpDecoder;
- MystPICT *_pictDecoder;
- JPEGDecoder *_jpegDecoder;
+ Graphics::PictDecoder *_pictDecoder;
+ Graphics::JPEGDecoder *_jpegDecoder;
Graphics::PixelFormat _pixelFormat;
struct PictureFile {
diff --git a/engines/mohawk/jpeg.cpp b/engines/mohawk/jpeg.cpp
deleted file mode 100644
index 07ec54dfea..0000000000
--- a/engines/mohawk/jpeg.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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/system.h"
-#include "graphics/conversion.h" // For YUV2RGB
-
-#include "mohawk/jpeg.h"
-
-namespace Mohawk {
-
-JPEGDecoder::JPEGDecoder(bool freeSurfaceAfterUse) : Graphics::Codec(), _freeSurfaceAfterUse(freeSurfaceAfterUse) {
- _jpeg = new Graphics::JPEG();
- _pixelFormat = g_system->getScreenFormat();
- _surface = NULL;
-}
-
-JPEGDecoder::~JPEGDecoder() {
- delete _jpeg;
-
- if (_surface) {
- _surface->free();
- delete _surface;
- }
-}
-
-Graphics::Surface *JPEGDecoder::decodeImage(Common::SeekableReadStream* stream) {
- _jpeg->read(stream);
- Graphics::Surface *ySurface = _jpeg->getComponent(1);
- Graphics::Surface *uSurface = _jpeg->getComponent(2);
- Graphics::Surface *vSurface = _jpeg->getComponent(3);
-
- Graphics::Surface *destSurface = NULL;
-
- // If we should free the surface after use, use the internal _surface storage
- // (this should be used when using as a Codec, as the Codecs should free their
- // surfaces when deleting the Codec object). Otherwise, create a new Surface
- // as the destination.
- if (_freeSurfaceAfterUse) {
- if (!_surface) {
- _surface = new Graphics::Surface();
- _surface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel);
- }
- destSurface = _surface;
- } else {
- destSurface = new Graphics::Surface();
- destSurface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel);
- }
-
- assert(destSurface);
-
- for (uint16 i = 0; i < destSurface->h; i++) {
- for (uint16 j = 0; j < destSurface->w; j++) {
- byte r = 0, g = 0, b = 0;
- Graphics::YUV2RGB(*((byte *)ySurface->getBasePtr(j, i)), *((byte *)uSurface->getBasePtr(j, i)), *((byte *)vSurface->getBasePtr(j, i)), r, g, b);
- if (_pixelFormat.bytesPerPixel == 2)
- *((uint16 *)destSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
- else
- *((uint32 *)destSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
- }
- }
-
- return destSurface;
-}
-
-} // End of namespace Mohawk
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index b224a8b143..bb79d4abac 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -6,11 +6,9 @@ MODULE_OBJS = \
detection.o \
dialogs.o \
graphics.o \
- jpeg.o \
livingbooks.o \
mohawk.o \
myst.o \
- myst_pict.o \
myst_vars.o \
myst_saveload.o \
myst_scripts.o \
@@ -22,13 +20,7 @@ MODULE_OBJS = \
riven_scripts.o \
riven_vars.o \
sound.o \
- video/cinepak.o \
- video/qdm2.o \
- video/qtrle.o \
- video/qt_player.o \
- video/rpza.o \
- video/smc.o \
- video/video.o
+ video.o
# This module can be built as a plugin
diff --git a/engines/mohawk/mohawk.cpp b/engines/mohawk/mohawk.cpp
index 5bde6bbeec..6b4d8bb2d2 100644
--- a/engines/mohawk/mohawk.cpp
+++ b/engines/mohawk/mohawk.cpp
@@ -35,7 +35,7 @@
#include "mohawk/mohawk.h"
#include "mohawk/dialogs.h"
#include "mohawk/sound.h"
-#include "mohawk/video/video.h"
+#include "mohawk/video.h"
#include "sound/mixer.h"
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index d1ef3b2137..9ff301c129 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -33,7 +33,7 @@
#include "mohawk/dialogs.h"
#include "mohawk/resource.h"
#include "mohawk/resource_cache.h"
-#include "mohawk/video/video.h"
+#include "mohawk/video.h"
namespace Mohawk {
diff --git a/engines/mohawk/myst_pict.cpp b/engines/mohawk/myst_pict.cpp
deleted file mode 100644
index 794ec9b87f..0000000000
--- a/engines/mohawk/myst_pict.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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/system.h"
-
-#include "mohawk/myst_pict.h"
-
-namespace Mohawk {
-
-// The PICT code is based off of the QuickDraw specs:
-// http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-461.html
-
-MystPICT::MystPICT(JPEGDecoder *jpegDecoder) {
- _jpegDecoder = jpegDecoder;
- _pixelFormat = g_system->getScreenFormat();
-}
-
-Graphics::Surface *MystPICT::decodeImage(Common::SeekableReadStream *stream) {
- // Skip initial 512 bytes (all 0's)
- stream->seek(512, SEEK_CUR);
-
- // Read in the first part of the header
- /* uint16 fileSize = */ stream->readUint16BE();
-
- _imageRect.top = stream->readUint16BE();
- _imageRect.left = stream->readUint16BE();
- _imageRect.bottom = stream->readUint16BE();
- _imageRect.right = stream->readUint16BE();
- _imageRect.debugPrint(0, "PICT Rect:");
-
- Graphics::Surface *image = new Graphics::Surface();
- image->create(_imageRect.width(), _imageRect.height(), _pixelFormat.bytesPerPixel);
-
- // NOTE: This is only a subset of the full PICT format.
- // - Only V2 Images Supported
- // - JPEG Chunks are Supported
- // - DirectBitsRect Chunks are Supported
- for (uint32 opNum = 0; !stream->eos() && !stream->err() && stream->pos() < stream->size(); opNum++) {
- uint16 opcode = stream->readUint16BE();
- debug(2, "Found PICT opcode %04x", opcode);
-
- if (opNum == 0 && opcode != 0x0011)
- error ("Cannot find PICT version opcode");
- else if (opNum == 1 && opcode != 0x0C00)
- error ("Cannot find PICT header opcode");
-
- if (opcode == 0x0000) { // Nop
- stream->readUint16BE(); // Unknown
- } else if (opcode == 0x0001) { // Clip
- // Ignore
- uint16 clipSize = stream->readUint16BE();
- stream->seek(clipSize - 2, SEEK_CUR);
- } else if (opcode == 0x0007) { // PnSize
- // Ignore
- stream->readUint16BE();
- stream->readUint16BE();
- } else if (opcode == 0x0011) { // VersionOp
- uint16 version = stream->readUint16BE();
- if (version != 0x02FF)
- error ("Unknown PICT version");
- } else if (opcode == 0x001E) { // DefHilite
- // Ignore, Contains no Data
- } else if (opcode == 0x009A) { // DirectBitsRect
- decodeDirectBitsRect(stream, image);
- } else if (opcode == 0x00A1) { // LongComment
- stream->readUint16BE();
- uint16 dataSize = stream->readUint16BE();
- stream->seek(dataSize, SEEK_CUR);
- } else if (opcode == 0x00FF) { // OpEndPic
- stream->readUint16BE();
- break;
- } else if (opcode == 0x0C00) { // HeaderOp
- /* uint16 version = */ stream->readUint16BE();
- stream->readUint16BE(); // Reserved
- /* uint32 hRes = */ stream->readUint32BE();
- /* uint32 vRes = */ stream->readUint32BE();
- Common::Rect origResRect;
- origResRect.top = stream->readUint16BE();
- origResRect.left = stream->readUint16BE();
- origResRect.bottom = stream->readUint16BE();
- origResRect.right = stream->readUint16BE();
- stream->readUint32BE(); // Reserved
- } else if (opcode == 0x8200) { // CompressedQuickTime
- decodeCompressedQuickTime(stream, image);
- break;
- } else {
- error ("Unknown PICT opcode %04x", opcode);
- }
- }
-
- return image;
-}
-
-struct DirectBitsRectData {
- // PixMap
- struct {
- uint32 baseAddr;
- uint16 rowBytes;
- Common::Rect bounds;
- uint16 pmVersion;
- uint16 packType;
- uint32 packSize;
- uint32 hRes;
- uint32 vRes;
- uint16 pixelType;
- uint16 pixelSize;
- uint16 cmpCount;
- uint16 cmpSize;
- uint32 planeBytes;
- uint32 pmTable;
- uint32 pmReserved;
- } pixMap;
- Common::Rect srcRect;
- Common::Rect dstRect;
- uint16 mode;
-};
-
-void MystPICT::decodeDirectBitsRect(Common::SeekableReadStream *stream, Graphics::Surface *image) {
- static const Graphics::PixelFormat directBitsFormat16 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
-
- DirectBitsRectData directBitsData;
- directBitsData.pixMap.baseAddr = stream->readUint32BE();
- directBitsData.pixMap.rowBytes = stream->readUint16BE() & 0x3fff;
- directBitsData.pixMap.bounds.top = stream->readUint16BE();
- directBitsData.pixMap.bounds.left = stream->readUint16BE();
- directBitsData.pixMap.bounds.bottom = stream->readUint16BE();
- directBitsData.pixMap.bounds.right = stream->readUint16BE();
- directBitsData.pixMap.pmVersion = stream->readUint16BE();
- directBitsData.pixMap.packType = stream->readUint16BE();
- directBitsData.pixMap.packSize = stream->readUint32BE();
- directBitsData.pixMap.hRes = stream->readUint32BE();
- directBitsData.pixMap.vRes = stream->readUint32BE();
- directBitsData.pixMap.pixelType = stream->readUint16BE();
- directBitsData.pixMap.pixelSize = stream->readUint16BE();
- directBitsData.pixMap.cmpCount = stream->readUint16BE();
- directBitsData.pixMap.cmpSize = stream->readUint16BE();
- directBitsData.pixMap.planeBytes = stream->readUint32BE();
- directBitsData.pixMap.pmTable = stream->readUint32BE();
- directBitsData.pixMap.pmReserved = stream->readUint32BE();
- directBitsData.srcRect.top = stream->readUint16BE();
- directBitsData.srcRect.left = stream->readUint16BE();
- directBitsData.srcRect.bottom = stream->readUint16BE();
- directBitsData.srcRect.right = stream->readUint16BE();
- directBitsData.dstRect.top = stream->readUint16BE();
- directBitsData.dstRect.left = stream->readUint16BE();
- directBitsData.dstRect.bottom = stream->readUint16BE();
- directBitsData.dstRect.right = stream->readUint16BE();
- directBitsData.mode = stream->readUint16BE();
-
- if (directBitsData.pixMap.pixelSize != 16 && directBitsData.pixMap.pixelSize != 32)
- error("Unhandled directBitsRect bitsPerPixel %d", directBitsData.pixMap.pixelSize);
-
- byte bytesPerPixel = (directBitsData.pixMap.pixelSize == 16) ? 2 : 3;
- byte *buffer = new byte[image->w * image->h * bytesPerPixel];
-
- // Read in amount of data per row
- for (uint16 i = 0; i < directBitsData.pixMap.bounds.height(); i++) {
- if (directBitsData.pixMap.packType == 1 || directBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte
- error("Pack Type = %d, Row Bytes = %d", directBitsData.pixMap.packType, directBitsData.pixMap.rowBytes);
- // TODO
- } else if (directBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte
- error("Pack Type = 2");
- // TODO
- } else if (directBitsData.pixMap.packType > 2) { // Packed
- uint16 byteCount = (directBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte();
- decodeDirectBitsLine(buffer + i * image->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), bytesPerPixel);
- }
- }
-
- if (bytesPerPixel == 2) {
- // Convert from 16-bit to whatever surface we need
- for (uint16 y = 0; y < image->h; y++) {
- for (uint16 x = 0; x < image->w; x++) {
- byte r = 0, g = 0, b = 0;
- uint32 color = READ_BE_UINT16(buffer + (y * image->w + x) * bytesPerPixel);
- directBitsFormat16.colorToRGB(color, r, g, b);
- *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
- }
- }
- } else {
- // Convert from 24-bit (planar!) to whatever surface we need
- for (uint16 y = 0; y < image->h; y++) {
- for (uint16 x = 0; x < image->w; x++) {
- byte r = *(buffer + y * image->w * 3 + x);
- byte g = *(buffer + y * image->w * 3 + image->w + x);
- byte b = *(buffer + y * image->w * 3 + image->w * 2 + x);
- *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
- }
- }
- }
-
- delete[] buffer;
-}
-
-void MystPICT::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel) {
- uint32 dataDecoded = 0;
- byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1;
-
- while (data->pos() < data->size() && dataDecoded < length) {
- byte op = data->readByte();
-
- if (op & 0x80) {
- uint32 runSize = (op ^ 255) + 2;
- uint16 value = (bytesPerDecode == 2) ? data->readUint16BE() : data->readByte();
-
- for (uint32 i = 0; i < runSize; i++) {
- if (bytesPerDecode == 2) {
- WRITE_BE_UINT16(out, value);
- out += 2;
- } else
- *out++ = value;
- }
- dataDecoded += runSize * bytesPerDecode;
- } else {
- uint32 runSize = (op + 1) * bytesPerDecode;
- for (uint32 i = 0; i < runSize; i++)
- *out++ = data->readByte();
- dataDecoded += runSize;
- }
- }
-
- // HACK: rowBytes is in 32-bit, but the data is 24-bit...
- if (bytesPerPixel == 3)
- dataDecoded += length / 4;
-
- if (length != dataDecoded)
- warning("Mismatched DirectBits read (%d/%d)", dataDecoded, length);
-
- delete data;
-}
-
-// Compressed QuickTime details can be found here:
-// http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html
-// http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html
-// I'm just ignoring that because Myst ME uses none of that extra stuff. The offset is always the same.
-
-void MystPICT::decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image) {
- uint32 dataSize = stream->readUint32BE();
- uint32 startPos = stream->pos();
-
- Graphics::Surface *jpegImage = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(stream, stream->pos() + 156, stream->pos() + dataSize));
- stream->seek(startPos + dataSize);
-
- image->copyFrom(*jpegImage);
-
- jpegImage->free();
- delete jpegImage;
-}
-
-} // End of namespace Mohawk
diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp
index a453bb0985..2f6d178da8 100644
--- a/engines/mohawk/myst_scripts.cpp
+++ b/engines/mohawk/myst_scripts.cpp
@@ -27,7 +27,7 @@
#include "mohawk/graphics.h"
#include "mohawk/myst_scripts.h"
#include "mohawk/sound.h"
-#include "mohawk/video/video.h"
+#include "mohawk/video.h"
#include "gui/message.h"
diff --git a/engines/mohawk/resource.cpp b/engines/mohawk/resource.cpp
index 62a857b90b..74efd6770f 100644
--- a/engines/mohawk/resource.cpp
+++ b/engines/mohawk/resource.cpp
@@ -103,7 +103,7 @@ void MohawkArchive::open(Common::SeekableReadStream *stream) {
else
debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x NameTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset, _types[i].name_table_offset);
- //Resource Table
+ // Resource Table
_mhk->seek(_rsrc.abs_offset + _types[i].resource_table_offset);
_types[i].resTable.resources = _mhk->readUint16BE();
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 0412144034..07b08dc220 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -29,39 +29,50 @@
#include "common/keyboard.h"
#include "mohawk/graphics.h"
+#include "mohawk/resource.h"
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
#include "mohawk/riven_saveload.h"
#include "mohawk/dialogs.h"
-#include "mohawk/video/video.h"
+#include "mohawk/video.h"
namespace Mohawk {
-Common::Rect *g_atrusJournalRectSolo;
-Common::Rect *g_atrusJournalRect;
-Common::Rect *g_cathJournalRect;
-Common::Rect *g_trapBookRect;
+Common::Rect *g_atrusJournalRect1;
+Common::Rect *g_atrusJournalRect2;
+Common::Rect *g_cathJournalRect2;
+Common::Rect *g_atrusJournalRect3;
+Common::Rect *g_cathJournalRect3;
+Common::Rect *g_trapBookRect3;
MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
_showHotspots = false;
_cardData.hasData = false;
_gameOver = false;
_activatedSLST = false;
+ _ignoreNextMouseUp = false;
_extrasFile = NULL;
+ _curStack = aspit;
+ _hotspots = NULL;
- // Attempt to let game run from the CDs
- // NOTE: assets2 contains higher quality audio than assets1
- const Common::FSNode gameDataDir(ConfMan.get("path"));
+ // NOTE: We can never really support CD swapping. All of the music files
+ // (*_Sounds.mhk) are stored on disc 1. They are copied to the hard drive
+ // during install and used from there. The same goes for the extras.mhk
+ // file. The following directories allow Riven to be played directly
+ // from the DVD.
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "all");
SearchMan.addSubDirectoryMatching(gameDataDir, "data");
SearchMan.addSubDirectoryMatching(gameDataDir, "exe");
- SearchMan.addSubDirectoryMatching(gameDataDir, "assets2");
-
- g_atrusJournalRectSolo = new Common::Rect(295, 402, 313, 426);
- g_atrusJournalRect = new Common::Rect(222, 402, 240, 426);
- g_cathJournalRect = new Common::Rect(291, 408, 311, 419);
- g_trapBookRect = new Common::Rect(363, 396, 386, 432);
+ SearchMan.addSubDirectoryMatching(gameDataDir, "assets1");
+
+ g_atrusJournalRect1 = new Common::Rect(295, 402, 313, 426);
+ g_atrusJournalRect2 = new Common::Rect(259, 402, 278, 426);
+ g_cathJournalRect2 = new Common::Rect(328, 408, 348, 419);
+ g_atrusJournalRect3 = new Common::Rect(222, 402, 240, 426);
+ g_cathJournalRect3 = new Common::Rect(291, 408, 311, 419);
+ g_trapBookRect3 = new Common::Rect(363, 396, 386, 432);
}
MohawkEngine_Riven::~MohawkEngine_Riven() {
@@ -70,15 +81,17 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
delete _externalScriptHandler;
delete _extrasFile;
delete _saveLoad;
+ delete _scriptMan;
delete[] _vars;
- delete _loadDialog;
delete _optionsDialog;
delete _rnd;
- delete g_atrusJournalRectSolo;
- delete g_atrusJournalRect;
- delete g_cathJournalRect;
- delete g_trapBookRect;
- _cardData.scripts.clear();
+ delete[] _hotspots;
+ delete g_atrusJournalRect1;
+ delete g_atrusJournalRect2;
+ delete g_cathJournalRect2;
+ delete g_atrusJournalRect3;
+ delete g_cathJournalRect3;
+ delete g_trapBookRect3;
}
GUI::Debugger *MohawkEngine_Riven::getDebugger() {
@@ -93,9 +106,8 @@ Common::Error MohawkEngine_Riven::run() {
_console = new RivenConsole(this);
_saveLoad = new RivenSaveLoad(this, _saveFileMan);
_externalScriptHandler = new RivenExternal(this);
- _loadDialog = new GUI::SaveLoadChooser("Load Game:", "Load");
- _loadDialog->setSaveMode(false);
_optionsDialog = new RivenOptionsDialog(this);
+ _scriptMan = new RivenScriptManager(this);
_rnd = new Common::RandomSource();
g_eventRec.registerRandomSource(*_rnd, "riven");
@@ -147,10 +159,15 @@ Common::Error MohawkEngine_Riven::run() {
runHotspotScript(_curHotspot, kMouseDownScript);
break;
case Common::EVENT_LBUTTONUP:
- if (_curHotspot >= 0)
- runHotspotScript(_curHotspot, kMouseUpScript);
- else
- checkInventoryClick();
+ // See RivenScript::switchCard() for more information on why we sometimes
+ // disable the next up event.
+ if (!_ignoreNextMouseUp) {
+ if (_curHotspot >= 0)
+ runHotspotScript(_curHotspot, kMouseUpScript);
+ else
+ checkInventoryClick();
+ }
+ _ignoreNextMouseUp = false;
break;
case Common::EVENT_KEYDOWN:
switch (event.kbd.keycode) {
@@ -233,6 +250,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
// Stop any videos playing
_video->stopVideos();
+ _video->clearMLST();
// Clear the old stack files out
for (uint32 i = 0; i < _mhk.size(); i++)
@@ -310,7 +328,6 @@ void MohawkEngine_Riven::refreshCard() {
_gfx->clearWaterEffects();
_gfx->_activatedPLSTs.clear();
_video->stopVideos();
- _video->_mlstRecords.clear();
_gfx->drawPLST(1);
_activatedSLST = false;
@@ -333,13 +350,13 @@ void MohawkEngine_Riven::refreshCard() {
}
void MohawkEngine_Riven::loadCard(uint16 id) {
- // NOTE: Do not clear the card scripts because it may delete a currently running script!
+ // NOTE: The card scripts are cleared by the RivenScriptManager automatically.
Common::SeekableReadStream* inStream = getRawData(ID_CARD, id);
_cardData.name = inStream->readSint16BE();
_cardData.zipModePlace = inStream->readUint16BE();
- _cardData.scripts = RivenScript::readScripts(this, inStream);
+ _cardData.scripts = _scriptMan->readScripts(inStream);
_cardData.hasData = true;
delete inStream;
@@ -357,7 +374,10 @@ void MohawkEngine_Riven::loadCard(uint16 id) {
}
void MohawkEngine_Riven::loadHotspots(uint16 id) {
- // NOTE: Do not clear the hotspots because it may delete a currently running script!
+ // Clear old hotspots
+ delete[] _hotspots;
+
+ // NOTE: The hotspot scripts are cleared by the RivenScriptManager automatically.
Common::SeekableReadStream* inStream = getRawData(ID_HSPT, id);
@@ -399,7 +419,7 @@ void MohawkEngine_Riven::loadHotspots(uint16 id) {
_hotspots[i].zipModeHotspot = inStream->readUint16BE();
// Read in the scripts now
- _hotspots[i].scripts = RivenScript::readScripts(this, inStream);
+ _hotspots[i].scripts = _scriptMan->readScripts(inStream);
}
delete inStream;
@@ -474,26 +494,37 @@ void MohawkEngine_Riven::checkInventoryClick() {
*matchVarToString("returncardid") = _curCard;
// See RivenGraphics::showInventory() for an explanation
- // of why only this variable is used.
+ // of the variables' meanings.
bool hasCathBook = *matchVarToString("acathbook") != 0;
+ bool hasTrapBook = *matchVarToString("atrapbook") != 0;
// Go to the book if a hotspot contains the mouse
if (!hasCathBook) {
- if (g_atrusJournalRectSolo->contains(_mousePos)) {
+ if (g_atrusJournalRect1->contains(_mousePos)) {
+ _gfx->hideInventory();
+ changeToStack(aspit);
+ changeToCard(5);
+ }
+ } else if (!hasTrapBook) {
+ if (g_atrusJournalRect2->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(5);
+ } else if (g_cathJournalRect2->contains(_mousePos)) {
+ _gfx->hideInventory();
+ changeToStack(aspit);
+ changeToCard(6);
}
} else {
- if (g_atrusJournalRect->contains(_mousePos)) {
+ if (g_atrusJournalRect3->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(5);
- } else if (g_cathJournalRect->contains(_mousePos)) {
+ } else if (g_cathJournalRect3->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(6);
- } else if (g_trapBookRect->contains(_mousePos)) {
+ } else if (g_trapBookRect3->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(7);
@@ -577,7 +608,19 @@ void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
}
void MohawkEngine_Riven::runLoadDialog() {
- runDialog(*_loadDialog);
+ GUI::SaveLoadChooser slc("Load Game:", "Load");
+ slc.setSaveMode(false);
+
+ Common::String gameId = ConfMan.get("gameid");
+
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+
+ int slot = slc.runModal(plugin, ConfMan.getActiveDomainName());
+ if (slot >= 0)
+ loadGameState(slot);
+
+ slc.close();
}
Common::Error MohawkEngine_Riven::loadGameState(int slot) {
@@ -612,4 +655,4 @@ bool ZipMode::operator== (const ZipMode &z) const {
return z.name == name && z.id == id;
}
-}
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index f014b76fb8..631285455e 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -68,10 +68,12 @@ enum {
// Rects for the inventory object positions (initialized in
// MohawkEngine_Riven's constructor).
-extern Common::Rect *g_atrusJournalRectSolo;
-extern Common::Rect *g_atrusJournalRect;
-extern Common::Rect *g_cathJournalRect;
-extern Common::Rect *g_trapBookRect;
+extern Common::Rect *g_atrusJournalRect1;
+extern Common::Rect *g_atrusJournalRect2;
+extern Common::Rect *g_cathJournalRect2;
+extern Common::Rect *g_atrusJournalRect3;
+extern Common::Rect *g_cathJournalRect3;
+extern Common::Rect *g_trapBookRect3;
struct RivenHotspot {
uint16 blstID;
@@ -111,9 +113,9 @@ public:
RivenGraphics *_gfx;
RivenExternal *_externalScriptHandler;
Common::RandomSource *_rnd;
+ RivenScriptManager *_scriptMan;
Card _cardData;
- bool _gameOver;
GUI::Debugger *getDebugger();
@@ -127,7 +129,6 @@ private:
MohawkArchive *_extrasFile; // We need a separate handle for the extra data
RivenConsole *_console;
RivenSaveLoad *_saveLoad;
- GUI::SaveLoadChooser *_loadDialog;
RivenOptionsDialog *_optionsDialog;
// Stack/Card-related functions and variables
@@ -147,6 +148,10 @@ private:
uint32 *_vars;
uint32 _varCount;
+ // Miscellaneous
+ bool _gameOver;
+ bool _ignoreNextMouseUp;
+
public:
Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id);
bool _activatedSLST;
@@ -180,6 +185,9 @@ public:
uint32 *getLocalVar(uint32 index);
uint32 *matchVarToString(Common::String varName);
uint32 *matchVarToString(const char *varName);
+
+ void setGameOver() { _gameOver = true; }
+ void ignoreNextMouseUp() { _ignoreNextMouseUp = true; }
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 99afacc5ce..67d621a54c 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -27,7 +27,7 @@
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
#include "mohawk/sound.h"
-#include "mohawk/video/video.h"
+#include "mohawk/video.h"
#include "gui/message.h"
#include "common/events.h"
@@ -210,7 +210,28 @@ void RivenExternal::runEndGame(uint16 video) {
_vm->_video->playMovieBlocking(video);
// TODO: Play until the last frame and then run the credits
- _vm->_gameOver = true;
+ _vm->setGameOver();
+}
+
+void RivenExternal::runDomeButtonMovie() {
+ // This command just plays the video of the button moving down and up.
+ _vm->_video->playMovieBlocking(2);
+}
+
+void RivenExternal::runDomeCheck() {
+ // Check if we clicked while the golden frame was showing
+
+ VideoHandle video = _vm->_video->findVideoHandle(1);
+ assert(video != NULL_VID_HANDLE);
+
+ int32 curFrame = _vm->_video->getCurFrame(video);
+ int32 frameCount = _vm->_video->getFrameCount(video);
+
+ // The final frame of the video is the 'golden' frame (double meaning: the
+ // frame that is the magic one is the one with the golden symbol) but we
+ // give a 3 frame leeway in either direction.
+ if (frameCount - curFrame < 3 || curFrame < 3)
+ *_vm->matchVarToString("domecheck") = 1;
}
// ------------------------------------------------------------------------------------
@@ -218,11 +239,13 @@ void RivenExternal::runEndGame(uint16 video) {
// ------------------------------------------------------------------------------------
void RivenExternal::xastartupbtnhide(uint16 argc, uint16 *argv) {
- // The original game hides the start/setup buttons depending on an ini entry. It's safe to ignore this command.
+ // The original game hides the start/setup buttons depending on an ini entry.
+ // It's safe to ignore this command.
}
void RivenExternal::xasetupcomplete(uint16 argc, uint16 *argv) {
- // The original game sets an ini entry to disable the setup button and use the start button only. It's safe to ignore this part of the command.
+ // The original game sets an ini entry to disable the setup button and use the
+ // start button only. It's safe to ignore this part of the command.
_vm->_sound->stopSound();
_vm->changeToCard(1);
}
@@ -261,9 +284,14 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ if (_vm->getFeatures() & GF_DEMO)
+ _vm->_sound->playSound(4, false);
+ else
+ _vm->_sound->playSound(3, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(1);
_vm->_gfx->updateScreen();
}
@@ -276,9 +304,14 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ if (_vm->getFeatures() & GF_DEMO)
+ _vm->_sound->playSound(5, false);
+ else
+ _vm->_sound->playSound(4, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(0);
_vm->_gfx->updateScreen();
}
@@ -326,9 +359,11 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(5, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(3);
_vm->_gfx->updateScreen();
}
@@ -341,9 +376,11 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(6, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(2);
_vm->_gfx->updateScreen();
}
@@ -357,12 +394,20 @@ void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {
void RivenExternal::xatrapbookclose(uint16 argc, uint16 *argv) {
// Close the trap book
*_vm->matchVarToString("atrap") = 0;
+
+ // Play the page turning sound
+ _vm->_sound->playSound(8, false);
+
_vm->refreshCard();
}
void RivenExternal::xatrapbookopen(uint16 argc, uint16 *argv) {
// Open the trap book
*_vm->matchVarToString("atrap") = 1;
+
+ // Play the page turning sound
+ _vm->_sound->playSound(9, false);
+
_vm->refreshCard();
}
@@ -414,7 +459,11 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
+ // Play the page turning sound
+ _vm->_sound->playSound(22, false);
+
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(1);
_vm->_gfx->updateScreen();
}
@@ -427,7 +476,11 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
+ // Play the page turning sound
+ _vm->_sound->playSound(23, false);
+
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(0);
_vm->_gfx->updateScreen();
}
@@ -514,13 +567,14 @@ void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
if (heat) {
if (platform == 0) {
_vm->_video->activateMLST(7, _vm->getCurCard());
- // TODO: Play video (non-blocking)
+ _vm->_video->playMovie(7);
} else {
_vm->_video->activateMLST(8, _vm->getCurCard());
- // TODO: Play video (non-blocking)
+ _vm->_video->playMovie(8);
}
} else {
- // TODO: Stop MLST's 7 and 8
+ _vm->_video->stopMovie(7);
+ _vm->_video->stopMovie(8);
}
_vm->refreshCard();
@@ -627,26 +681,28 @@ void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) {
}
void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xbisland_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
// Get the variable for the valve
uint32 *valve = _vm->matchVarToString("bvalve");
- Common::Event event;
int changeX = 0;
int changeY = 0;
+ bool done = false;
// Set the cursor to the closed position
_vm->_gfx->changeCursor(2004);
_vm->_system->updateScreen();
- for (;;) {
+ while (!done) {
+ Common::Event event;
+
while (_vm->_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
@@ -658,30 +714,53 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
// FIXME: These values for changes in x/y could be tweaked.
if (*valve == 0 && changeY <= -10) {
*valve = 1;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(2);
_vm->refreshCard();
} else if (*valve == 1) {
if (changeX >= 0 && changeY >= 10) {
*valve = 0;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(3);
_vm->refreshCard();
} else if (changeX <= -10 && changeY <= 10) {
*valve = 2;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(1);
_vm->refreshCard();
}
} else if (*valve == 2 && changeX >= 10) {
*valve = 1;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(4);
_vm->refreshCard();
}
- return;
+ done = true;
default:
break;
}
}
_vm->_system->delayMillis(10);
}
+
+ // If we changed state and the new state is that the valve is flowing to
+ // the boiler, we need to update the boiler state.
+ if (*valve == 1) {
+ if (*_vm->matchVarToString("bidvlv") == 1) { // Check which way the water is going at the boiler
+ if (*_vm->matchVarToString("bblrarm") == 1) {
+ // If the pipe is open, make sure the water is drained out
+ *_vm->matchVarToString("bheat") = 0;
+ *_vm->matchVarToString("bblrwtr") = 0;
+ } else {
+ // If the pipe is closed, fill the boiler again
+ *_vm->matchVarToString("bheat") = *_vm->matchVarToString("bblrvalve");
+ *_vm->matchVarToString("bblrwtr") = 1;
+ }
+ } else {
+ // Have the grating inside the boiler match the switch outside
+ *_vm->matchVarToString("bblrgrt") = (*_vm->matchVarToString("bblrsw") == 1) ? 0 : 1;
+ }
+ }
}
void RivenExternal::xbchipper(uint16 argc, uint16 *argv) {
@@ -723,11 +802,11 @@ void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) {
}
void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) {
@@ -936,33 +1015,60 @@ void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
}
void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
- // TODO: This function is supposed to do a lot more, something like this (pseudocode):
-
- // Show level pull movie
- // Set transition up
- // Change to up card
- // Show movie of carriage coming down
- // Set transition down
- // Change back to card 276
- // Show movie of carriage coming down
- // if jgallows == 0
- // Set up timer
- // Enter new input loop
- // if you click within the time
- // move forward
- // set transition right
- // change to card right
- // show movie of ascending
- // change to card 263
- // else
- // show movie of carriage ascending only
- // else
- // show movie of carriage ascending only
-
-
- // For now, if the gallows base is closed, assume ascension and move to that card.
- if (*_vm->matchVarToString("jgallows") == 0)
- _vm->changeToCard(_vm->matchRMAPToCard(0x17167));
+ // Run the gallows's carriage
+
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_video->playMovieBlocking(1); // Play handle movie
+ _vm->_gfx->scheduleTransition(15); // Set pan down transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x18e77)); // Change to card facing up
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor (again)
+ _vm->_video->playMovieBlocking(4); // Play carriage beginning to drop
+ _vm->_gfx->scheduleTransition(14); // Set pan up transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x183a9)); // Change to card looking straight again
+ _vm->_video->playMovieBlocking(2);
+
+ uint32 *gallows = _vm->matchVarToString("jgallows");
+ if (*gallows == 1) {
+ // If the gallows is open, play the up movie and return
+ _vm->_video->playMovieBlocking(3);
+ return;
+ }
+
+ // Give the player 5 seconds to click (anywhere)
+ uint32 startTime = _vm->_system->getMillis();
+ bool gotClick = false;
+ while (_vm->_system->getMillis() - startTime <= 5000 && !gotClick) {
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ _vm->_system->updateScreen();
+ break;
+ case Common::EVENT_LBUTTONUP:
+ gotClick = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ _vm->_system->delayMillis(10);
+ }
+
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+
+ if (gotClick) {
+ _vm->_gfx->scheduleTransition(16); // Schedule dissolve transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x18d4d)); // Move forward
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_system->delayMillis(500); // Delay a half second before changing again
+ _vm->_gfx->scheduleTransition(12); // Schedule pan left transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x18ab5)); // Turn right
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_video->playMovieBlocking(1); // Play carriage ride movie
+ _vm->changeToCard(_vm->matchRMAPToCard(0x17167)); // We have arrived at the top
+ } else
+ _vm->_video->playMovieBlocking(3); // Too slow!
}
void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
@@ -978,11 +1084,11 @@ void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) {
}
void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xjisland3500_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
int RivenExternal::jspitElevatorLoop() {
@@ -1019,8 +1125,10 @@ int RivenExternal::jspitElevatorLoop() {
void RivenExternal::xhandlecontrolup(uint16 argc, uint16 *argv) {
int changeLevel = jspitElevatorLoop();
+ // If we've moved the handle down, go down a floor
if (changeLevel == -1) {
- // TODO: Run movie
+ _vm->_video->playMovieBlocking(1);
+ _vm->_video->playMovieBlocking(2);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e374));
}
}
@@ -1028,8 +1136,10 @@ void RivenExternal::xhandlecontrolup(uint16 argc, uint16 *argv) {
void RivenExternal::xhandlecontroldown(uint16 argc, uint16 *argv) {
int changeLevel = jspitElevatorLoop();
+ // If we've moved the handle up, go up a floor
if (changeLevel == 1) {
- // TODO: Run movie
+ _vm->_video->playMovieBlocking(1);
+ _vm->_video->playMovieBlocking(2);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e374));
}
}
@@ -1037,11 +1147,29 @@ void RivenExternal::xhandlecontroldown(uint16 argc, uint16 *argv) {
void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {
int changeLevel = jspitElevatorLoop();
+ if (changeLevel == 0)
+ return;
+
+ // Play the handle moving video
+ if (changeLevel == 1)
+ _vm->_video->playMovieBlocking(7);
+ else
+ _vm->_video->playMovieBlocking(6);
+
+ // If the whark's mouth is open, close it
+ uint32 *mouthVar = _vm->matchVarToString("jwmouth");
+ if (*mouthVar == 1) {
+ _vm->_video->playMovieBlocking(3);
+ _vm->_video->playMovieBlocking(8);
+ *mouthVar = 0;
+ }
+
+ // Play the elevator video and then change the card
if (changeLevel == 1) {
- // TODO: Run movie
+ _vm->_video->playMovieBlocking(5);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e597));
- } else if (changeLevel == -1) {
- // TODO: Run movie
+ } else {
+ _vm->_video->playMovieBlocking(4);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e29c));
}
}
@@ -1172,9 +1300,11 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(12, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(1);
_vm->_gfx->updateScreen();
}
@@ -1187,13 +1317,15 @@ void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(13, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(0);
_vm->_gfx->updateScreen();
}
-static uint16 getComboDigit(uint32 correctCombo, uint32 digit) {
+uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) {
static const uint32 powers[] = { 100000, 10000, 1000, 100, 10, 1 };
return (correctCombo % powers[digit]) / powers[digit + 1];
}
@@ -1258,11 +1390,11 @@ void RivenExternal::xpisland990_elevcombo(uint16 argc, uint16 *argv) {
}
void RivenExternal::xpscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) {
@@ -1457,11 +1589,11 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
}
void RivenExternal::xtscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
index 8270a00854..bdf3fa01bc 100644
--- a/engines/mohawk/riven_external.h
+++ b/engines/mohawk/riven_external.h
@@ -38,6 +38,7 @@ public:
~RivenExternal();
void runCommand(uint16 argc, uint16 *argv);
+ uint16 getComboDigit(uint32 correctCombo, uint32 digit);
private:
MohawkEngine_Riven *_vm;
@@ -57,6 +58,8 @@ private:
int jspitElevatorLoop();
void runDemoBoundaryDialog();
void runEndGame(uint16 video);
+ void runDomeCheck();
+ void runDomeButtonMovie();
// -----------------------------------------------------
// aspit (Main Menu, Books, Setup) external commands
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 3c989838da..d73b4ec0dc 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "mohawk/resource.h"
#include "mohawk/riven.h"
#include "mohawk/riven_saveload.h"
@@ -31,11 +32,9 @@
namespace Mohawk {
RivenSaveLoad::RivenSaveLoad(MohawkEngine_Riven *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) {
- _loadFile = new MohawkArchive();
}
RivenSaveLoad::~RivenSaveLoad() {
- delete _loadFile;
}
Common::StringArray RivenSaveLoad::generateSaveGameList() {
@@ -63,7 +62,8 @@ static uint16 mapOldStackIDToNew(uint16 oldID) {
case 8:
return aspit;
}
- error ("Unknown old stack ID %d", oldID);
+
+ error("Unknown old stack ID %d", oldID);
return 0;
}
@@ -86,7 +86,8 @@ static uint16 mapNewStackIDToOld(uint16 newID) {
case tspit:
return 4;
}
- error ("Unknown new stack ID %d", newID);
+
+ error("Unknown new stack ID %d", newID);
return 0;
}
@@ -94,26 +95,28 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
if (_vm->getFeatures() & GF_DEMO) // Don't load games in the demo
return false;
- Common::InSaveFile *loadFile;
- if (!(loadFile = _saveFileMan->openForLoading(filename.c_str())))
+ Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
+ if (!loadFile)
return false;
- debug (0, "Loading game from \'%s\'", filename.c_str());
- _loadFile->open(loadFile);
+ debug(0, "Loading game from \'%s\'", filename.c_str());
+
+ MohawkArchive *mhk = new MohawkArchive();
+ mhk->open(loadFile);
// First, let's make sure we're using a saved game file from this version of Riven by checking the VERS resource
- Common::SeekableReadStream *vers = _loadFile->getRawData(ID_VERS, 1);
+ Common::SeekableReadStream *vers = mhk->getRawData(ID_VERS, 1);
uint32 saveGameVersion = vers->readUint32BE();
delete vers;
if ((saveGameVersion == kCDSaveGameVersion && (_vm->getFeatures() & GF_DVD))
|| (saveGameVersion == kDVDSaveGameVersion && !(_vm->getFeatures() & GF_DVD))) {
- warning ("Incompatible saved game versions. No support for this yet.");
- delete _loadFile;
+ warning("Incompatible saved game versions. No support for this yet.");
+ delete mhk;
return false;
}
// Now, we'll read in the variable values.
- Common::SeekableReadStream *vars = _loadFile->getRawData(ID_VARS, 1);
+ Common::SeekableReadStream *vars = mhk->getRawData(ID_VARS, 1);
Common::Array<uint32> rawVariables;
while (!vars->eos()) {
@@ -126,7 +129,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
// Next, we set the variables based on the name found by the index in the VARS resource.
// TODO: Merge with code in mohawk.cpp for loading names?
- Common::SeekableReadStream *names = _loadFile->getRawData(ID_NAME, 1);
+ Common::SeekableReadStream *names = mhk->getRawData(ID_NAME, 1);
uint16 namesCount = names->readUint16BE();
uint16 *stringOffsets = new uint16[namesCount];
@@ -151,9 +154,10 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
c = (char)names->readByte();
}
- // WORKAROUND: Some versions have two extra variables. However, the saves are
- // still compatible with other saves of the same version. Are these used in the
- // original interpreter anywhere? (They come from DVD v1.1)
+ // TODO: Some versions have two extra variables. However, the saves are
+ // still compatible with other saves of the same version (they come from DVD v1.1).
+ // There are used in the whark number puzzle. I thought jleftpos and jrightpos were
+ // for this purpose.
if (name == "dropLeftStart" || name == "dropRightStart")
continue;
@@ -161,11 +165,11 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
*var = rawVariables[i];
- if (!scumm_stricmp(name.c_str(), "CurrentStackID"))
+ if (name.equalsIgnoreCase("CurrentStackID"))
stackID = mapOldStackIDToNew(rawVariables[i]);
- else if (!scumm_stricmp(name.c_str(), "CurrentCardID"))
+ else if (name.equalsIgnoreCase("CurrentCardID"))
cardID = rawVariables[i];
- else if (!scumm_stricmp(name.c_str(), "ReturnStackID"))
+ else if (name.equalsIgnoreCase("ReturnStackID"))
*var = mapOldStackIDToNew(rawVariables[i]);
}
@@ -179,7 +183,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
_vm->_zipModeData.clear();
// Finally, we load in zip mode data.
- Common::SeekableReadStream *zips = _loadFile->getRawData(ID_ZIPS, 1);
+ Common::SeekableReadStream *zips = mhk->getRawData(ID_ZIPS, 1);
uint16 zipsRecordCount = zips->readUint16BE();
for (uint16 i = 0; i < zipsRecordCount; i++) {
ZipMode zip;
@@ -189,10 +193,10 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
zip.id = zips->readUint16BE();
_vm->_zipModeData.push_back(zip);
}
+
delete zips;
+ delete mhk;
- delete _loadFile;
- _loadFile = NULL;
return true;
}
@@ -211,7 +215,14 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genVARSSection() {
for (uint32 i = 0; i < _vm->getVarCount(); i++) {
stream->writeUint32BE(0); // Unknown
stream->writeUint32BE(0); // Unknown
- stream->writeUint32BE(_vm->getGlobalVar(i));
+
+ // Remap returnstackid here because we don't actually want to change
+ // our internal returnstackid.
+ uint32 variable = _vm->getGlobalVar(i);
+ if (_vm->getGlobalVarName(i) == "returnstackid")
+ variable = mapNewStackIDToOld(variable);
+
+ stream->writeUint32BE(variable);
}
return stream;
@@ -257,17 +268,17 @@ bool RivenSaveLoad::saveGame(Common::String filename) {
// Note, this code is still WIP. It works quite well for now.
// Make sure we have the right extension
- if (!filename.hasSuffix(".rvn") && !filename.hasSuffix(".RVN"))
+ if (!filename.matchString("*.rvn", true))
filename += ".rvn";
// Convert class variables to variable numbers
*_vm->matchVarToString("currentstackid") = mapNewStackIDToOld(_vm->getCurStack());
*_vm->matchVarToString("currentcardid") = _vm->getCurCard();
- *_vm->matchVarToString("returnstackid") = mapNewStackIDToOld(*_vm->matchVarToString("returnstackid"));
- Common::OutSaveFile *saveFile;
- if (!(saveFile = _saveFileMan->openForSaving(filename.c_str())))
+ Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
+ if (!saveFile)
return false;
+
debug (0, "Saving game to \'%s\'", filename.c_str());
Common::MemoryWriteStreamDynamic *versSection = genVERSSection();
@@ -392,7 +403,7 @@ bool RivenSaveLoad::saveGame(Common::String filename) {
void RivenSaveLoad::deleteSave(Common::String saveName) {
debug (0, "Deleting save file \'%s\'", saveName.c_str());
- _saveFileMan->removeSavefile(saveName.c_str());
+ _saveFileMan->removeSavefile(saveName);
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_saveload.h b/engines/mohawk/riven_saveload.h
index c708f9d35d..9cc9bc3737 100644
--- a/engines/mohawk/riven_saveload.h
+++ b/engines/mohawk/riven_saveload.h
@@ -29,8 +29,6 @@
#include "common/savefile.h"
#include "common/str.h"
-#include "mohawk/resource.h"
-
namespace Mohawk {
class MohawkEngine_Riven;
@@ -53,7 +51,6 @@ public:
private:
MohawkEngine_Riven *_vm;
Common::SaveFileManager *_saveFileMan;
- MohawkArchive *_loadFile;
Common::MemoryWriteStreamDynamic *genVERSSection();
Common::MemoryWriteStreamDynamic *genNAMESection();
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index e809ad9642..1fcaba8ac0 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -28,40 +28,28 @@
#include "mohawk/riven_external.h"
#include "mohawk/riven_scripts.h"
#include "mohawk/sound.h"
-#include "mohawk/video/video.h"
+#include "mohawk/video.h"
#include "common/stream.h"
#include "graphics/cursorman.h"
namespace Mohawk {
-RivenScript::RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType)
- : _vm(vm), _stream(stream), _scriptType(scriptType) {
+RivenScript::RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard)
+ : _vm(vm), _stream(stream), _scriptType(scriptType), _parentStack(parentStack), _parentCard(parentCard) {
setupOpcodes();
+ _isRunning = false;
}
RivenScript::~RivenScript() {
delete _stream;
}
-RivenScriptList RivenScript::readScripts(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream) {
- RivenScriptList scriptList;
-
- uint16 scriptCount = stream->readUint16BE();
- for (uint16 i = 0; i < scriptCount; i++) {
- uint16 scriptType = stream->readUint16BE();
- uint32 scriptSize = calculateScriptSize(stream);
- scriptList.push_back(Common::SharedPtr<RivenScript>(new RivenScript(vm, stream->readStream(scriptSize), scriptType)));
- }
-
- return scriptList;
-}
-
uint32 RivenScript::calculateCommandSize(Common::SeekableReadStream* script) {
uint16 command = script->readUint16BE();
uint32 commandSize = 2;
if (command == 8) {
- if (script->readUint16BE() != 2)
+ if (script->readUint16BE() != 2) // Arg count?
warning ("if-then-else unknown value is not 2");
script->readUint16BE(); // variable to check against
uint16 logicBlockCount = script->readUint16BE(); // number of logic blocks
@@ -161,7 +149,7 @@ void RivenScript::setupOpcodes() {
OPCODE(activateFLST),
OPCODE(zipMode),
OPCODE(activateMLST),
- OPCODE(activateSLSTWithVolume)
+ OPCODE(empty) // Activate an SLST with a volume parameter (not used)
};
_opcodes = riven_opcodes;
@@ -239,10 +227,13 @@ void RivenScript::dumpCommands(Common::StringArray varNames, Common::StringArray
}
void RivenScript::runScript() {
+ _isRunning = true;
+
if (_stream->pos() != 0)
_stream->seek(0);
processCommands(true);
+ _isRunning = false;
}
void RivenScript::processCommands(bool runCommands) {
@@ -298,13 +289,10 @@ void RivenScript::processCommands(bool runCommands) {
// Command 1: draw tBMP resource (tbmp_id, left, top, right, bottom, u0, u1, u2, u3)
void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
- if (argc < 5) {
- // Copy the image to the whole screen, ignoring the rest of the parameters
+ if (argc < 5) // Copy the image to the whole screen, ignoring the rest of the parameters
_vm->_gfx->copyImageToScreen(argv[0], 0, 0, 608, 392);
- } else {
- // Copy the image to a certain part of the screen
+ else // Copy the image to a certain part of the screen
_vm->_gfx->copyImageToScreen(argv[0], argv[1], argv[2], argv[3], argv[4]);
- }
// Now, update the screen
_vm->_gfx->updateScreen();
@@ -313,6 +301,12 @@ void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
// Command 2: go to card (card id)
void RivenScript::switchCard(uint16 op, uint16 argc, uint16 *argv) {
_vm->changeToCard(argv[0]);
+
+ // WORKAROUND: If we changed card on a mouse down event,
+ // we want to ignore the next mouse up event so we don't
+ // change card when lifting the mouse on the next card.
+ if (_scriptType == kMouseDownScript)
+ _vm->ignoreNextMouseUp();
}
// Command 3: play an SLST from the script
@@ -547,9 +541,8 @@ void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
// Command 41: activate MLST record and play
void RivenScript::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
- _vm->_video->enableMovie(argv[0] - 1);
_vm->_video->activateMLST(argv[0], _vm->getCurCard());
- // TODO: Play movie (blocking?)
+ _vm->_video->playMovie(argv[0]);
}
// Command 43: activate BLST record (card hotspot enabling lists)
@@ -608,9 +601,46 @@ void RivenScript::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
_vm->_video->activateMLST(argv[0], _vm->getCurCard());
}
-// Command 47: activate SLST record with a volume argument
-void RivenScript::activateSLSTWithVolume(uint16 op, uint16 argc, uint16 *argv) {
- warning("STUB: activateSLSTWithVolume()");
+RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) {
+ _vm = vm;
+}
+
+RivenScriptManager::~RivenScriptManager() {
+ for (uint32 i = 0; i < _currentScripts.size(); i++)
+ delete _currentScripts[i];
+}
+
+RivenScriptList RivenScriptManager::readScripts(Common::SeekableReadStream *stream, bool garbageCollect) {
+ if (garbageCollect)
+ unloadUnusedScripts(); // Garbage collect!
+
+ RivenScriptList scriptList;
+
+ uint16 scriptCount = stream->readUint16BE();
+ for (uint16 i = 0; i < scriptCount; i++) {
+ uint16 scriptType = stream->readUint16BE();
+ uint32 scriptSize = RivenScript::calculateScriptSize(stream);
+ RivenScript *script = new RivenScript(_vm, stream->readStream(scriptSize), scriptType, _vm->getCurStack(), _vm->getCurCard());
+ scriptList.push_back(script);
+
+ // Only add it to the scripts that we will free later if it is requested.
+ // (ie. we don't want to store scripts from the dumpScript console command)
+ if (garbageCollect)
+ _currentScripts.push_back(script);
+ }
+
+ return scriptList;
+}
+
+void RivenScriptManager::unloadUnusedScripts() {
+ // Free any scripts that aren't part of the current card and aren't running
+ for (uint32 i = 0; i < _currentScripts.size(); i++) {
+ if ((_vm->getCurStack() != _currentScripts[i]->getParentStack() || _vm->getCurCard() != _currentScripts[i]->getParentCard()) && !_currentScripts[i]->isRunning()) {
+ delete _currentScripts[i];
+ _currentScripts.remove_at(i);
+ i--;
+ }
+ }
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index a1512af697..5187bbde08 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -50,19 +50,20 @@ enum {
};
class RivenScript;
-typedef Common::Array<Common::SharedPtr<RivenScript> > RivenScriptList;
class RivenScript {
public:
- RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType);
+ RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard);
~RivenScript();
void runScript();
void dumpScript(Common::StringArray varNames, Common::StringArray xNames, byte tabs);
uint16 getScriptType() { return _scriptType; }
+ uint16 getParentStack() { return _parentStack; }
+ uint16 getParentCard() { return _parentCard; }
+ bool isRunning() { return _isRunning; }
- // Read in an array of script objects from a stream
- static RivenScriptList readScripts(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream);
+ static uint32 calculateScriptSize(Common::SeekableReadStream *script);
private:
typedef void (RivenScript::*OpcodeProcRiven)(uint16 op, uint16 argc, uint16 *argv);
@@ -70,18 +71,18 @@ private:
OpcodeProcRiven proc;
const char *desc;
};
- const RivenOpcode* _opcodes;
+ const RivenOpcode *_opcodes;
void setupOpcodes();
MohawkEngine_Riven *_vm;
Common::SeekableReadStream *_stream;
- uint16 _scriptType;
+ uint16 _scriptType, _parentStack, _parentCard, _parentHotspot;
+ bool _isRunning;
void dumpCommands(Common::StringArray varNames, Common::StringArray xNames, byte tabs);
void processCommands(bool runCommands);
- static uint32 calculateCommandSize(Common::SeekableReadStream* script);
- static uint32 calculateScriptSize(Common::SeekableReadStream* script);
+ static uint32 calculateCommandSize(Common::SeekableReadStream *script);
DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); }
@@ -120,7 +121,21 @@ private:
DECLARE_OPCODE(activateFLST);
DECLARE_OPCODE(zipMode);
DECLARE_OPCODE(activateMLST);
- DECLARE_OPCODE(activateSLSTWithVolume);
+};
+
+typedef Common::Array<RivenScript*> RivenScriptList;
+
+class RivenScriptManager {
+public:
+ RivenScriptManager(MohawkEngine_Riven *vm);
+ ~RivenScriptManager();
+
+ RivenScriptList readScripts(Common::SeekableReadStream *stream, bool garbageCollect = true);
+
+private:
+ void unloadUnusedScripts();
+ RivenScriptList _currentScripts;
+ MohawkEngine_Riven *_vm;
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index 3f7cd5a8b6..b6d2dff315 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -319,6 +319,8 @@ void MohawkEngine_Riven::initVars() {
*matchVarToString("bheat") = 1;
*matchVarToString("waterenabled") = 1;
*matchVarToString("ogehnpage") = 1;
+ *matchVarToString("bblrsw") = 1;
+ *matchVarToString("ocage") = 1;
// Randomize the telescope combination
uint32 *teleCombo = matchVarToString("tcorrectorder");
diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index b84573f011..091bd68021 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -27,12 +27,12 @@
#include "common/util.h"
+#include "sound/musicplugin.h"
#include "sound/audiostream.h"
#include "sound/decoders/mp3.h"
#include "sound/decoders/raw.h"
#include "sound/decoders/wave.h"
-
namespace Mohawk {
Sound::Sound(MohawkEngine* vm) : _vm(vm) {
@@ -79,7 +79,7 @@ void Sound::initMidi() {
// Let's get our MIDI parser/driver
_midiParser = MidiParser::createParser_SMF();
- _midiDriver = MidiDriver::createMidi(MidiDriver::detectMusicDriver(MDT_ADLIB|MDT_MIDI));
+ _midiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB|MDT_MIDI));
// Set up everything!
_midiDriver->open();
@@ -233,6 +233,10 @@ void Sound::playSLST(uint16 index, uint16 card) {
if (slstRecord.index == index) {
playSLST(slstRecord);
+ delete[] slstRecord.sound_ids;
+ delete[] slstRecord.volumes;
+ delete[] slstRecord.balances;
+ delete[] slstRecord.u2;
delete slstStream;
return;
}
@@ -244,6 +248,7 @@ void Sound::playSLST(uint16 index, uint16 card) {
}
delete slstStream;
+
// No matching records, assume we need to stop all SLST's
stopAllSLST();
}
@@ -277,8 +282,11 @@ void Sound::playSLST(SLSTRecord slstRecord) {
}
void Sound::stopAllSLST() {
- for (uint16 i = 0; i < _currentSLSTSounds.size(); i++)
+ for (uint16 i = 0; i < _currentSLSTSounds.size(); i++) {
_vm->_mixer->stopHandle(*_currentSLSTSounds[i].handle);
+ delete _currentSLSTSounds[i].handle;
+ }
+
_currentSLSTSounds.clear();
}
@@ -314,6 +322,7 @@ void Sound::playSLSTSound(uint16 id, bool fade, bool loop, uint16 volume, int16
void Sound::stopSLSTSound(uint16 index, bool fade) {
// TODO: Fade out, mixer needs to be extended to get volume on a handle
_vm->_mixer->stopHandle(*_currentSLSTSounds[index].handle);
+ delete _currentSLSTSounds[index].handle;
_currentSLSTSounds.remove_at(index);
}
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
new file mode 100644
index 0000000000..17456b8ec3
--- /dev/null
+++ b/engines/mohawk/video.cpp
@@ -0,0 +1,415 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "mohawk/resource.h"
+#include "mohawk/video.h"
+
+#include "common/events.h"
+#include "graphics/video/qt_decoder.h"
+
+namespace Mohawk {
+
+VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) {
+}
+
+VideoManager::~VideoManager() {
+ stopVideos();
+}
+
+void VideoManager::pauseVideos() {
+ for (uint16 i = 0; i < _videoStreams.size(); i++)
+ if (_videoStreams[i].video)
+ _videoStreams[i]->pauseVideo(true);
+}
+
+void VideoManager::resumeVideos() {
+ for (uint16 i = 0; i < _videoStreams.size(); i++)
+ if (_videoStreams[i].video)
+ _videoStreams[i]->pauseVideo(false);
+}
+
+void VideoManager::stopVideos() {
+ for (uint16 i = 0; i < _videoStreams.size(); i++)
+ delete _videoStreams[i].video;
+ _videoStreams.clear();
+}
+
+void VideoManager::playMovie(Common::String filename, uint16 x, uint16 y, bool clearScreen) {
+ VideoHandle videoHandle = createVideoHandle(filename, x, y, false);
+ if (videoHandle == NULL_VID_HANDLE)
+ return;
+
+ // Clear screen if requested
+ if (clearScreen) {
+ _vm->_system->fillScreen(_vm->_system->getScreenFormat().RGBToColor(0, 0, 0));
+ _vm->_system->updateScreen();
+ }
+
+ waitUntilMovieEnds(videoHandle);
+}
+
+void VideoManager::playMovieCentered(Common::String filename, bool clearScreen) {
+ VideoHandle videoHandle = createVideoHandle(filename, 0, 0, false);
+ if (videoHandle == NULL_VID_HANDLE)
+ return;
+
+ // Clear screen if requested
+ if (clearScreen) {
+ _vm->_system->fillScreen(_vm->_system->getScreenFormat().RGBToColor(0, 0, 0));
+ _vm->_system->updateScreen();
+ }
+
+ _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2;
+ _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2;
+
+ waitUntilMovieEnds(videoHandle);
+}
+
+void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
+ bool continuePlaying = true;
+
+ while (_videoStreams[videoHandle].video && !_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) {
+ if (updateBackgroundMovies())
+ _vm->_system->updateScreen();
+
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RTL:
+ case Common::EVENT_QUIT:
+ continuePlaying = false;
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_SPACE:
+ _vm->pauseGame();
+ break;
+ case Common::KEYCODE_ESCAPE:
+ continuePlaying = false;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Cut down on CPU usage
+ _vm->_system->delayMillis(10);
+ }
+
+ delete _videoStreams[videoHandle].video;
+ _videoStreams[videoHandle].video = 0;
+ _videoStreams[videoHandle].id = 0;
+ _videoStreams[videoHandle].filename.clear();
+}
+
+void VideoManager::playBackgroundMovie(Common::String filename, int16 x, int16 y, bool loop) {
+ VideoHandle videoHandle = createVideoHandle(filename, x, y, loop);
+ if (videoHandle == NULL_VID_HANDLE)
+ return;
+
+ // Center x if requested
+ if (x < 0)
+ _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2;
+
+ // Center y if requested
+ if (y < 0)
+ _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2;
+}
+
+bool VideoManager::updateBackgroundMovies() {
+ bool updateScreen = false;
+
+ for (uint32 i = 0; i < _videoStreams.size() && !_vm->shouldQuit(); i++) {
+ // Skip deleted videos
+ if (!_videoStreams[i].video)
+ continue;
+
+ // Remove any videos that are over
+ if (_videoStreams[i]->endOfVideo()) {
+ if (_videoStreams[i].loop) {
+ _videoStreams[i]->rewind();
+ } else {
+ delete _videoStreams[i].video;
+ _videoStreams[i].video = 0;
+ _videoStreams[i].id = 0;
+ _videoStreams[i].filename.clear();
+ continue;
+ }
+ }
+
+ // Check if we need to draw a frame
+ if (_videoStreams[i]->needsUpdate()) {
+ Graphics::Surface *frame = _videoStreams[i]->decodeNextFrame();
+ bool deleteFrame = false;
+
+ if (frame && _videoStreams[i].enabled) {
+ // Convert from 8bpp to the current screen format if necessary
+ if (frame->bytesPerPixel == 1) {
+ Graphics::Surface *newFrame = new Graphics::Surface();
+ Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat();
+ byte *palette = _videoStreams[i]->getPalette();
+ assert(palette);
+
+ newFrame->create(frame->w, frame->h, pixelFormat.bytesPerPixel);
+
+ for (uint16 j = 0; j < frame->h; j++) {
+ for (uint16 k = 0; k < frame->w; k++) {
+ byte palIndex = *((byte *)frame->getBasePtr(k, j));
+ byte r = palette[palIndex * 3];
+ byte g = palette[palIndex * 3 + 1];
+ byte b = palette[palIndex * 3 + 2];
+ if (pixelFormat.bytesPerPixel == 2)
+ *((uint16 *)newFrame->getBasePtr(k, j)) = pixelFormat.RGBToColor(r, g, b);
+ else
+ *((uint32 *)newFrame->getBasePtr(k, j)) = pixelFormat.RGBToColor(r, g, b);
+ }
+ }
+
+ frame = newFrame;
+ deleteFrame = true;
+ }
+
+ // Clip the width/height to make sure we stay on the screen (Myst does this a few times)
+ uint16 width = MIN<int32>(_videoStreams[i]->getWidth(), _vm->_system->getWidth() - _videoStreams[i].x);
+ uint16 height = MIN<int32>(_videoStreams[i]->getHeight(), _vm->_system->getHeight() - _videoStreams[i].y);
+ _vm->_system->copyRectToScreen((byte*)frame->pixels, frame->pitch, _videoStreams[i].x, _videoStreams[i].y, width, height);
+
+ // We've drawn something to the screen, make sure we update it
+ updateScreen = true;
+
+ // Delete the frame if we're using the buffer from the 8bpp conversion
+ if (deleteFrame) {
+ frame->free();
+ delete frame;
+ }
+ }
+ }
+
+ // Update the audio buffer too
+ _videoStreams[i]->updateAudioBuffer();
+ }
+
+ // Return true if we need to update the screen
+ return updateScreen;
+}
+
+void VideoManager::activateMLST(uint16 mlstId, uint16 card) {
+ Common::SeekableReadStream *mlstStream = _vm->getRawData(ID_MLST, card);
+ uint16 recordCount = mlstStream->readUint16BE();
+
+ for (uint16 i = 0; i < recordCount; i++) {
+ MLSTRecord mlstRecord;
+ mlstRecord.index = mlstStream->readUint16BE();
+ mlstRecord.movieID = mlstStream->readUint16BE();
+ mlstRecord.code = mlstStream->readUint16BE();
+ mlstRecord.left = mlstStream->readUint16BE();
+ mlstRecord.top = mlstStream->readUint16BE();
+
+ for (byte j = 0; j < 2; j++)
+ if (mlstStream->readUint16BE() != 0)
+ warning("u0[%d] in MLST non-zero", j);
+
+ if (mlstStream->readUint16BE() != 0xFFFF)
+ warning("u0[2] in MLST not 0xFFFF");
+
+ mlstRecord.loop = mlstStream->readUint16BE();
+ mlstRecord.volume = mlstStream->readUint16BE();
+ mlstRecord.u1 = mlstStream->readUint16BE();
+
+ if (mlstRecord.u1 != 1)
+ warning("mlstRecord.u1 not 1");
+
+ // We've found a match, add it
+ if (mlstRecord.index == mlstId) {
+ // Make sure we don't have any duplicates
+ for (uint32 j = 0; j < _mlstRecords.size(); j++)
+ if (_mlstRecords[j].index == mlstRecord.index || _mlstRecords[j].code == mlstRecord.code) {
+ _mlstRecords.remove_at(j);
+ j--;
+ }
+
+ _mlstRecords.push_back(mlstRecord);
+ break;
+ }
+ }
+
+ delete mlstStream;
+}
+
+void VideoManager::clearMLST() {
+ _mlstRecords.clear();
+}
+
+void VideoManager::playMovie(uint16 id) {
+ for (uint16 i = 0; i < _mlstRecords.size(); i++)
+ if (_mlstRecords[i].code == id) {
+ debug(1, "Play tMOV %d (non-blocking) at (%d, %d) %s", _mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].loop != 0 ? "looping" : "non-looping");
+ createVideoHandle(_mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].loop != 0);
+ return;
+ }
+}
+
+void VideoManager::playMovieBlocking(uint16 id) {
+ for (uint16 i = 0; i < _mlstRecords.size(); i++)
+ if (_mlstRecords[i].code == id) {
+ debug(1, "Play tMOV %d (blocking) at (%d, %d)", _mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top);
+ VideoHandle videoHandle = createVideoHandle(_mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, false);
+ waitUntilMovieEnds(videoHandle);
+ return;
+ }
+}
+
+void VideoManager::stopMovie(uint16 id) {
+ debug(2, "Stopping movie %d", id);
+ for (uint16 i = 0; i < _mlstRecords.size(); i++)
+ if (_mlstRecords[i].code == id)
+ for (uint16 j = 0; j < _videoStreams.size(); j++)
+ if (_mlstRecords[i].movieID == _videoStreams[j].id) {
+ delete _videoStreams[j].video;
+ _videoStreams[j].video = 0;
+ _videoStreams[j].id = 0;
+ _videoStreams[j].filename.clear();
+ return;
+ }
+}
+
+void VideoManager::enableMovie(uint16 id) {
+ debug(2, "Enabling movie %d", id);
+ for (uint16 i = 0; i < _mlstRecords.size(); i++)
+ if (_mlstRecords[i].code == id)
+ for (uint16 j = 0; j < _videoStreams.size(); j++)
+ if (_mlstRecords[i].movieID == _videoStreams[j].id) {
+ _videoStreams[j].enabled = true;
+ return;
+ }
+}
+
+void VideoManager::disableMovie(uint16 id) {
+ debug(2, "Disabling movie %d", id);
+ for (uint16 i = 0; i < _mlstRecords.size(); i++)
+ if (_mlstRecords[i].code == id)
+ for (uint16 j = 0; j < _videoStreams.size(); j++)
+ if (_mlstRecords[i].movieID == _videoStreams[j].id) {
+ _videoStreams[j].enabled = false;
+ return;
+ }
+}
+
+void VideoManager::disableAllMovies() {
+ debug(2, "Disabling all movies");
+ for (uint16 i = 0; i < _videoStreams.size(); i++)
+ _videoStreams[i].enabled = false;
+}
+
+VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop) {
+ // First, check to see if that video is already playing
+ for (uint32 i = 0; i < _videoStreams.size(); i++)
+ if (_videoStreams[i].id == id)
+ return i;
+
+ // Otherwise, create a new entry
+ VideoEntry entry;
+ entry.video = new Graphics::QuickTimeDecoder();
+ entry.x = x;
+ entry.y = y;
+ entry.filename = "";
+ entry.id = id;
+ entry.loop = loop;
+ entry.enabled = true;
+ entry->setChunkBeginOffset(_vm->getResourceOffset(ID_TMOV, id));
+ entry->load(_vm->getRawData(ID_TMOV, id));
+
+ // Search for any deleted videos so we can take a formerly used slot
+ for (uint32 i = 0; i < _videoStreams.size(); i++)
+ if (!_videoStreams[i].video) {
+ _videoStreams[i] = entry;
+ return i;
+ }
+
+ // Otherwise, just add it to the list
+ _videoStreams.push_back(entry);
+ return _videoStreams.size() - 1;
+}
+
+VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop) {
+ // First, check to see if that video is already playing
+ for (uint32 i = 0; i < _videoStreams.size(); i++)
+ if (_videoStreams[i].filename == filename)
+ return i;
+
+ // Otherwise, create a new entry
+ VideoEntry entry;
+ entry.video = new Graphics::QuickTimeDecoder();
+ entry.x = x;
+ entry.y = y;
+ entry.filename = filename;
+ entry.id = 0;
+ entry.loop = loop;
+ entry.enabled = true;
+
+ Common::File *file = new Common::File();
+ if (!file->open(filename)) {
+ delete file;
+ return NULL_VID_HANDLE;
+ }
+
+ entry->load(file);
+
+ // Search for any deleted videos so we can take a formerly used slot
+ for (uint32 i = 0; i < _videoStreams.size(); i++)
+ if (!_videoStreams[i].video) {
+ _videoStreams[i] = entry;
+ return i;
+ }
+
+ // Otherwise, just add it to the list
+ _videoStreams.push_back(entry);
+ return _videoStreams.size() - 1;
+}
+
+VideoHandle VideoManager::findVideoHandle(uint16 id) {
+ for (uint16 i = 0; i < _mlstRecords.size(); i++)
+ if (_mlstRecords[i].code == id)
+ for (uint16 j = 0; j < _videoStreams.size(); j++)
+ if (_videoStreams[j].video && _mlstRecords[i].movieID == _videoStreams[j].id)
+ return j;
+
+ return NULL_VID_HANDLE;
+}
+
+int32 VideoManager::getCurFrame(const VideoHandle &handle) {
+ assert(handle != NULL_VID_HANDLE);
+ return _videoStreams[handle]->getCurFrame();
+}
+
+uint32 VideoManager::getFrameCount(const VideoHandle &handle) {
+ assert(handle != NULL_VID_HANDLE);
+ return _videoStreams[handle]->getFrameCount();
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
new file mode 100644
index 0000000000..6aa553e26b
--- /dev/null
+++ b/engines/mohawk/video.h
@@ -0,0 +1,114 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 MOHAWK_VIDEO_H
+#define MOHAWK_VIDEO_H
+
+#include "graphics/pixelformat.h"
+
+namespace Graphics {
+ class QuickTimeDecoder;
+}
+
+namespace Mohawk {
+
+class MohawkEngine;
+
+struct MLSTRecord {
+ uint16 index;
+ uint16 movieID;
+ uint16 code;
+ uint16 left;
+ uint16 top;
+ uint16 u0[3];
+ uint16 loop;
+ uint16 volume;
+ uint16 u1;
+};
+
+struct VideoEntry {
+ Graphics::QuickTimeDecoder *video;
+ uint16 x;
+ uint16 y;
+ bool loop;
+ Common::String filename;
+ uint16 id; // Riven only
+ bool enabled;
+
+ Graphics::QuickTimeDecoder *operator->() const { assert(video); return video; }
+};
+
+typedef int32 VideoHandle;
+
+enum {
+ NULL_VID_HANDLE = -1
+};
+
+class VideoManager {
+public:
+ VideoManager(MohawkEngine *vm);
+ ~VideoManager();
+
+ // Generic movie functions
+ void playMovie(Common::String filename, uint16 x = 0, uint16 y = 0, bool clearScreen = false);
+ void playMovieCentered(Common::String filename, bool clearScreen = true);
+ void playBackgroundMovie(Common::String filename, int16 x = -1, int16 y = -1, bool loop = false);
+ bool updateBackgroundMovies();
+ void pauseVideos();
+ void resumeVideos();
+ void stopVideos();
+
+ // Riven-related functions
+ void activateMLST(uint16 mlstId, uint16 card);
+ void clearMLST();
+ void enableMovie(uint16 id);
+ void disableMovie(uint16 id);
+ void disableAllMovies();
+ void playMovie(uint16 id);
+ void stopMovie(uint16 id);
+ void playMovieBlocking(uint16 id);
+
+ // Handle functions
+ VideoHandle findVideoHandle(uint16 id);
+ int32 getCurFrame(const VideoHandle &handle);
+ uint32 getFrameCount(const VideoHandle &handle);
+
+private:
+ MohawkEngine *_vm;
+
+ // Riven-related variables
+ Common::Array<MLSTRecord> _mlstRecords;
+
+ // Keep tabs on any videos playing
+ Common::Array<VideoEntry> _videoStreams;
+
+ VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop);
+ VideoHandle createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop);
+ void waitUntilMovieEnds(VideoHandle videoHandle);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp
index a493523301..cfe6ad8954 100644
--- a/engines/parallaction/callables_ns.cpp
+++ b/engines/parallaction/callables_ns.cpp
@@ -453,14 +453,15 @@ void Parallaction_ns::_c_startIntro(void *parm) {
}
void Parallaction_ns::_c_endIntro(void *parm) {
- // NOTE: suspend command execution queue, to
- // avoid running the QUIT command before
- // credits are displayed. This solves bug
- // #2619824.
- // Execution of the command list will resume
- // as soon as runGameFrame is run.
- _cmdExec->suspend();
-
+ if (getFeatures() & GF_DEMO) {
+ // NOTE: suspend command execution queue, to
+ // avoid running the QUIT command before
+ // credits are displayed. This solves bug
+ // #2619824.
+ // Execution of the command list will resume
+ // as soon as runGameFrame is run.
+ _cmdExec->suspend();
+ }
startCreditSequence();
_intro = false;
}
diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp
index e5e2b22644..e00a087923 100644
--- a/engines/parallaction/detection.cpp
+++ b/engines/parallaction/detection.cpp
@@ -240,7 +240,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOLAUNCHLOAD
+ Common::GUIO_NOLAUNCHLOAD,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class ParallactionMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/parallaction/exec.cpp b/engines/parallaction/exec.cpp
index 24579286e6..434c4f6ae6 100644
--- a/engines/parallaction/exec.cpp
+++ b/engines/parallaction/exec.cpp
@@ -193,8 +193,10 @@ void CommandExec::runSuspended() {
debugC(3, kDebugExec, "CommandExec::runSuspended()");
_execZone = _suspendedCtxt._zone;
- runList(_suspendedCtxt._first, _suspendedCtxt._last);
+ CommandList::iterator first = _suspendedCtxt._first;
+ CommandList::iterator last = _suspendedCtxt._last;
cleanSuspendedList();
+ runList(first, last);
}
}
diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp
index d5d89616d2..13c1318123 100644
--- a/engines/parallaction/exec_br.cpp
+++ b/engines/parallaction/exec_br.cpp
@@ -293,7 +293,7 @@ DECLARE_COMMAND_OPCODE(text) {
DECLARE_COMMAND_OPCODE(part) {
- warning("Parallaction_br::cmdOp_part not yet implemented");
+ _vm->_nextPart = ctxt._cmd->_counterValue;
}
@@ -527,6 +527,11 @@ DECLARE_INSTRUCTION_OPCODE(endif) {
DECLARE_INSTRUCTION_OPCODE(stop) {
ZonePtr z = ctxt._inst->_z;
+
+ // Prevent execution if zone is missing. The known case is "PART2/insegui.scr", which has
+ // "STOP insegui", which doesn't exist (see ticket #3021744 for the gory details)
+ if (!z) return;
+
if (ACTIONTYPE(z) == kZoneHear) {
warning("Parallaction_br::instOp_stop not yet implemented for HEAR zones");
// TODO: stop music or sound effects generated by a zone.
diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp
index fc6cb28d9e..a1926fc197 100644
--- a/engines/parallaction/gfxbase.cpp
+++ b/engines/parallaction/gfxbase.cpp
@@ -222,10 +222,12 @@ void Gfx::drawGfxObject(GfxObj *obj, Graphics::Surface &surf) {
obj->getRect(obj->frame, rect);
int x = obj->x;
+ int y = obj->y;
if (_overlayMode) {
x += _scrollPosX;
+ y += _scrollPosY;
}
- rect.translate(x, obj->y);
+ rect.translate(x, y);
data = obj->getData(obj->frame);
if (obj->getSize(obj->frame) == obj->getRawSize(obj->frame)) {
@@ -281,30 +283,54 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
return;
}
- Common::Rect q(r);
- Common::Rect clipper(surf->w, surf->h);
- q.clip(clipper);
- if (!q.isValidRect()) return;
-
- uint inc = r.width() * (100 - scale);
- uint thr = r.width() * 100;
- uint xAccum = 0, yAccum = 0;
+ // unscaled rectangle size
+ uint width = r.width();
+ uint height = r.height();
+
+ // scaled rectangle size
+ uint scaledWidth = r.width() * scale / 100;
+ uint scaledHeight = r.height() * scale / 100;
+
+ // scaled rectangle origin
+ uint scaledLeft = r.left + (width - scaledWidth) / 2;
+ uint scaledTop = r.top + (height - scaledHeight);
+
+ // clipped scaled destination rectangle
+ Common::Rect dstRect(scaledWidth, scaledHeight);
+ dstRect.moveTo(scaledLeft, scaledTop);
+
+ Common::Rect clipper(surf->w, surf->h);
+ dstRect.clip(clipper);
+ if (!dstRect.isValidRect()) return;
+
+
+ // clipped source rectangle
+ Common::Rect srcRect;
+ srcRect.left = (dstRect.left - scaledLeft) * 100 / scale;
+ srcRect.top = (dstRect.top - scaledTop) * 100 / scale;
+ srcRect.setWidth(dstRect.width() * 100 / scale);
+ srcRect.setHeight(dstRect.height() * 100 / scale);
+ if (!srcRect.isValidRect()) return;
Common::Point dp;
- dp.x = q.left + (r.width() * (100 - scale)) / 200;
- dp.y = q.top + (r.height() * (100 - scale)) / 100;
- q.translate(-r.left, -r.top);
- byte *s = data + q.left + q.top * r.width();
+ dp.x = dstRect.left;
+ dp.y = dstRect.top;
+
+ byte *s = data + srcRect.left + srcRect.top * width;
byte *d = (byte*)surf->getBasePtr(dp.x, dp.y);
uint line = 0, col = 0;
- for (uint16 i = 0; i < q.height(); i++) {
+ uint xAccum = 0, yAccum = 0;
+ uint inc = width * (100 - scale);
+ uint thr = width * 100;
+
+ for (uint16 i = 0; i < srcRect.height(); i++) {
yAccum += inc;
if (yAccum >= thr) {
yAccum -= thr;
- s += r.width();
+ s += width;
continue;
}
@@ -312,7 +338,7 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
byte *d2 = d;
col = 0;
- for (uint16 j = 0; j < q.width(); j++) {
+ for (uint16 j = 0; j < srcRect.width(); j++) {
xAccum += inc;
if (xAccum >= thr) {
@@ -335,7 +361,7 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
col++;
}
- s += r.width() - q.width();
+ s += width - srcRect.width();
d += surf->w;
line++;
}
diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp
index a7b1f6fe32..326ae2c519 100644
--- a/engines/parallaction/graphics.cpp
+++ b/engines/parallaction/graphics.cpp
@@ -317,8 +317,10 @@ void Gfx::drawList(Graphics::Surface &surface, GfxObjArray &list) {
void Gfx::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
if (_doubleBuffering) {
- if (_overlayMode)
+ if (_overlayMode) {
x += _scrollPosX;
+ y += _scrollPosY;
+ }
byte *dst = (byte*)_backBuffer.getBasePtr(x, y);
for (int i = 0; i < h; i++) {
@@ -358,7 +360,7 @@ void Gfx::unlockScreen() {
void Gfx::updateScreenIntern() {
if (_doubleBuffering) {
- byte *data = (byte*)_backBuffer.getBasePtr(_scrollPosX, 0);
+ byte *data = (byte*)_backBuffer.getBasePtr(_scrollPosX, _scrollPosY);
_vm->_system->copyRectToScreen(data, _backBuffer.pitch, 0, 0, _vm->_screenWidth, _vm->_screenHeight);
}
@@ -863,6 +865,8 @@ void Gfx::setBackground(uint type, BackgroundInfo *info) {
_minScrollX = 0;
_maxScrollX = MAX<int>(0, _backgroundInfo->width - _vm->_screenWidth);
+ _minScrollY = 0;
+ _maxScrollY = MAX<int>(0, _backgroundInfo->height - _vm->_screenHeight);
}
diff --git a/engines/parallaction/gui_br.cpp b/engines/parallaction/gui_br.cpp
index 7096bbe569..21cb2d8b00 100644
--- a/engines/parallaction/gui_br.cpp
+++ b/engines/parallaction/gui_br.cpp
@@ -98,6 +98,12 @@ public:
}
};
+
+struct LocationPart {
+ int part;
+ const char *location;
+};
+
class MainMenuInputState_BR : public MenuInputState {
Parallaction_br *_vm;
@@ -158,7 +164,7 @@ class MainMenuInputState_BR : public MenuInputState {
const char **_menuStrings;
const MenuOptions *_options;
- static const char *_firstLocation[];
+ static LocationPart _firstLocation[];
int _availItems;
int _selection;
@@ -205,7 +211,8 @@ public:
return this;
}
- switch (_options[_selection]) {
+ int selection = _options[_selection];
+ switch (selection) {
case kMenuQuit: {
_vm->quitGame();
break;
@@ -218,8 +225,10 @@ public:
}
break;
- default:
- _vm->scheduleLocationSwitch(_firstLocation[_options[_selection]]);
+ default:
+ _vm->_nextPart = _firstLocation[selection].part;
+ _vm->scheduleLocationSwitch(_firstLocation[selection].location);
+
}
_vm->_system->showMouse(false);
@@ -262,14 +271,15 @@ public:
};
-const char *MainMenuInputState_BR::_firstLocation[] = {
- "intro.0",
- "museo.1",
- "start.2",
- "bolscoi.3",
- "treno.4"
+LocationPart MainMenuInputState_BR::_firstLocation[] = {
+ { 0, "intro" },
+ { 1, "museo" },
+ { 2, "start" },
+ { 3, "bolscoi" },
+ { 4, "treno" }
};
+
const char *MainMenuInputState_BR::_menuStringsAmiga[NUM_MENULINES] = {
"See the introduction",
"Load a Saved Game",
diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp
index 60cc867226..7ad1be8681 100644
--- a/engines/parallaction/input.cpp
+++ b/engines/parallaction/input.cpp
@@ -148,8 +148,7 @@ void Input::readInput() {
setCursorPos(e.mouse);
}
- if (_vm->_debugger->isAttached())
- _vm->_debugger->onFrame();
+ _vm->_debugger->onFrame();
return;
@@ -390,7 +389,7 @@ void Input::exitInventoryMode() {
_vm->dropItem(z->u._mergeObj1);
_vm->dropItem(z->u._mergeObj2);
_vm->addInventoryItem(z->u._mergeObj3);
- _vm->_cmdExec->run(z->_commands);
+ _vm->_cmdExec->run(z->_commands); // commands might set a new _inputMode
}
}
@@ -407,7 +406,11 @@ void Input::exitInventoryMode() {
}
_vm->resumeJobs();
- _inputMode = kInputModeGame;
+ // in case the input mode was not changed by the code above (especially by the commands
+ // executed in case of a merge), then assume we are going back to game mode
+ if (_inputMode == kInputModeInventory) {
+ _inputMode = kInputModeGame;
+ }
}
bool Input::updateInventoryInput() {
diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h
index a5769350bd..3a84aa215e 100644
--- a/engines/parallaction/parallaction.h
+++ b/engines/parallaction/parallaction.h
@@ -546,6 +546,9 @@ public:
const char **_audioCommandsNamesRes;
static const char *_partNames[];
int _part;
+ int _nextPart;
+
+
#if 0 // disabled since I couldn't find any references to lip sync in the scripts
int16 _lipSyncVal;
uint _subtitleLipSync;
diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp
index 9fd46cc9af..ee718189b5 100644
--- a/engines/parallaction/parallaction_br.cpp
+++ b/engines/parallaction/parallaction_br.cpp
@@ -61,8 +61,8 @@ Common::Error Parallaction_br::init() {
_disk = new DosDisk_br(this);
}
_disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters.
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ MidiDriver *driver = MidiDriver::createMidi(dev);
_soundManI = new DosSoundMan_br(this, driver);
} else {
_disk = new AmigaDisk_br(this);
@@ -86,6 +86,7 @@ Common::Error Parallaction_br::init() {
_walker = new PathWalker_BR;
_part = -1;
+ _nextPart = -1;
_subtitle[0] = 0;
_subtitle[1] = 0;
@@ -260,6 +261,12 @@ void Parallaction_br::cleanupGame() {
_globalFlagsNames = 0;
_objectsNames = 0;
_countersNames = 0;
+
+ _numLocations = 0;
+ _globalFlags = 0;
+ memset(_localFlags, 0, sizeof(_localFlags));
+ memset(_locationNames, 0, sizeof(_locationNames));
+ memset(_zoneFlags, 0, sizeof(_zoneFlags));
}
@@ -268,17 +275,16 @@ void Parallaction_br::changeLocation() {
return;
}
- char location[200];
- strcpy(location, _newLocationName.c_str());
-
- char *partStr = strrchr(location, '.');
- if (partStr) {
+ if (_nextPart != -1) {
cleanupGame();
- int n = partStr - location;
- location[n] = '\0';
+ // more cleanup needed for part changes (see also saveload)
+ _globalFlags = 0;
+ cleanInventory(true);
+ strcpy(_characterName1, "null");
+
+ _part = _nextPart;
- _part = atoi(++partStr);
if (getFeatures() & GF_DEMO) {
assert(_part == 1);
} else {
@@ -305,8 +311,8 @@ void Parallaction_br::changeLocation() {
freeLocation(false);
// load new location
- strcpy(_location._name, location);
- parseLocation(location);
+ strcpy(_location._name, _newLocationName.c_str());
+ parseLocation(_location._name);
if (_location._startPosition.x != -1000) {
_char._ani->setFoot(_location._startPosition);
@@ -357,6 +363,7 @@ void Parallaction_br::changeLocation() {
_engineFlags &= ~kEngineChangeLocation;
_newLocationName.clear();
+ _nextPart = -1;
}
// FIXME: Parallaction_br::parseLocation() is now a verbatim copy of the same routine from Parallaction_ns.
diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp
index c1d6c9367a..f1e7b14583 100644
--- a/engines/parallaction/parallaction_ns.cpp
+++ b/engines/parallaction/parallaction_ns.cpp
@@ -24,7 +24,6 @@
*/
#include "common/system.h"
-
#include "common/config-manager.h"
#include "parallaction/parallaction.h"
@@ -167,8 +166,8 @@ Common::Error Parallaction_ns::init() {
_disk->init();
if (getPlatform() == Common::kPlatformPC) {
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ MidiDriver *driver = MidiDriver::createMidi(dev);
_soundManI = new DosSoundMan_ns(this, driver);
_soundManI->setMusicVolume(ConfMan.getInt("music_volume"));
} else {
diff --git a/engines/parallaction/parser.h b/engines/parallaction/parser.h
index 3e46e99180..5eb26e9fa1 100644
--- a/engines/parallaction/parser.h
+++ b/engines/parallaction/parser.h
@@ -294,6 +294,7 @@ public:
virtual void parseGetData(ZonePtr z);
virtual void parseDoorData(ZonePtr z);
virtual void parseHearData(ZonePtr z);
+ virtual void parseNoneData(ZonePtr z);
protected:
void parseAnswerCounter(Answer *answer);
virtual Answer *parseAnswer();
diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp
index d3ce1235c1..57259fd637 100644
--- a/engines/parallaction/parser_br.cpp
+++ b/engines/parallaction/parser_br.cpp
@@ -825,6 +825,16 @@ void LocationParser_br::parseHearData(ZonePtr z) {
}
}
+void LocationParser_br::parseNoneData(ZonePtr z) {
+ /* the only case we have to handle here is that of "scende2", which is the only Animation with
+ a command list following the type marker.
+ */
+ if (!scumm_stricmp(_tokens[0], "commands")) {
+ parseCommands(z->_commands);
+ }
+}
+
+
typedef void (LocationParser_br::*ZoneTypeParser)(ZonePtr);
static ZoneTypeParser parsers[] = {
0, // no type
@@ -836,7 +846,7 @@ static ZoneTypeParser parsers[] = {
&LocationParser_br::parseHearData,
0, // feel
&LocationParser_br::parseSpeakData,
- 0, // none
+ &LocationParser_br::parseNoneData,
0, // trap
0, // you
0, // command
@@ -882,7 +892,6 @@ DECLARE_ANIM_PARSER(moveto) {
// ctxt.a->_moveTo.z = atoi(_tokens[3]);
}
-
DECLARE_ANIM_PARSER(endanimation) {
debugC(7, kDebugParser, "ANIM_PARSER(endanimation) ");
diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp
index 8fc916e490..d6df23d415 100644
--- a/engines/parallaction/walk.cpp
+++ b/engines/parallaction/walk.cpp
@@ -450,7 +450,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
Common::Point foot;
s._a->getFoot(foot);
- debugC(1, kDebugWalk, "buildPath: from (%i, %i) to (%i, %i)", foot.x, foot.y, x, y);
+ debugC(1, kDebugWalk, "buildPath: try to build path from (%i, %i) to (%i, %i)", foot.x, foot.y, x, y);
s._walkPath.clear();
// look for easy path first
@@ -465,13 +465,13 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
ZonePtr z0 = _vm->hitZone(kZonePath, x, y);
if (!z0) {
s._walkPath.push_back(dest);
- debugC(3, kDebugWalk, "buildPath: corner case 0");
+ debugC(3, kDebugWalk, "buildPath: corner case 0 (%i nodes)", s._walkPath.size());
return;
}
ZonePtr z1 = _vm->hitZone(kZonePath, foot.x, foot.y);
if (!z1 || z1 == z0) {
s._walkPath.push_back(dest);
- debugC(3, kDebugWalk, "buildPath: corner case 1");
+ debugC(3, kDebugWalk, "buildPath: corner case 1 (%i nodes)", s._walkPath.size());
return;
}
@@ -480,7 +480,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
if (z1->u._pathLists[id].empty()) {
s._walkPath.clear();
- debugC(3, kDebugWalk, "buildPath: no path");
+ debugC(3, kDebugWalk, "buildPath: no path found");
return;
}
@@ -490,7 +490,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
s._walkPath.push_front(*b);
}
s._walkPath.push_back(dest);
- debugC(3, kDebugWalk, "buildPath: complex path");
+ debugC(3, kDebugWalk, "buildPath: complex path (%i nodes)", s._walkPath.size());
}
@@ -541,8 +541,6 @@ void PathWalker_BR::walk() {
return;
}
- debugC(3, kDebugWalk, "PathWalker_BR::walk()");
-
doWalk(_character);
doWalk(_follower);
@@ -566,8 +564,6 @@ void PathWalker_BR::walk() {
}
_vm->_gfx->initiateScroll(dx, dy);
-
- debugC(3, kDebugWalk, "PathWalker_BR::walk() -> done");
}
void PathWalker_BR::checkTrap(const Common::Point &p) {
@@ -601,8 +597,6 @@ void PathWalker_BR::doWalk(State &s) {
return;
}
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk(%s)", s._a->_name);
-
if (s._walkDelay > 0) {
s._walkDelay--;
if (s._walkDelay == 0 && s._a->_scriptName) {
@@ -619,10 +613,10 @@ void PathWalker_BR::doWalk(State &s) {
if (s._walkPath.empty()) {
finalizeWalk(s);
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk, case 0");
+ debugC(3, kDebugWalk, "PathWalker_BR::doWalk, walk completed (no more nodes)");
return;
} else {
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk, moving to next node");
+ debugC(3, kDebugWalk, "PathWalker_BR::doWalk, reached a walkpath node, %i left", s._walkPath.size());
}
}
@@ -632,6 +626,19 @@ void PathWalker_BR::doWalk(State &s) {
int xStep = (scale * 16) / 100 + 1;
int yStep = (scale * 10) / 100 + 1;
+
+ /* WORKAROUND: in the balloon scene, the position of the balloon (which is implemented as a
+ Character) is controlled by the user (for movement, via this walking code) and by the scripts
+ (to simulate the balloon floating in the air, in a neverending loop that alters the position
+ coordinates).
+ When the two step sizes are equal in magnitude and opposite in direction, then the walk code
+ enters an infinite loop without giving control back to the user (this happens quite frequently
+ when navigating the balloon near the borders of the screen, where the calculated step is
+ forcibly small because of clipping). Since the "floating" script (part1/scripts/mongolo.scr)
+ uses increments of 3 for both x and y, we tweak the calculated steps accordingly here. */
+ if (xStep == 3) xStep--;
+ if (yStep == 3) yStep--;
+
debugC(9, kDebugWalk, "calculated step: (%i, %i)", xStep, yStep);
s._fieldC = 0;
@@ -714,7 +721,7 @@ void PathWalker_BR::doWalk(State &s) {
Common::Point p2;
s._a->getFoot(p2);
checkTrap(p2);
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk, case 1");
+ debugC(3, kDebugWalk, "PathWalker_BR::doWalk, stepped to (%i, %i)", p2.x, p2.y);
return;
}
diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp
index b4b9210616..3d859c8335 100644
--- a/engines/queen/music.cpp
+++ b/engines/queen/music.cpp
@@ -34,6 +34,7 @@
#include "sound/midiparser.h"
+
namespace Queen {
extern MidiDriver *C_Player_CreateAdLibMidiDriver(Audio::Mixer *);
@@ -45,9 +46,9 @@ MidiMusic::MidiMusic(QueenEngine *vm)
_queuePos = _lastSong = _currentSong = 0;
queueClear();
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _adlib = (midiDriver == MD_ADLIB);
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
+ _adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
const char *musicDataFile;
if (vm->resource()->isDemo()) {
@@ -72,7 +73,7 @@ MidiMusic::MidiMusic(QueenEngine *vm)
// }
_driver = C_Player_CreateAdLibMidiDriver(vm->_mixer);
} else {
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (_nativeMT32) {
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
}
diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp
index 10c3d56cb4..74bb52f574 100644
--- a/engines/queen/queen.cpp
+++ b/engines/queen/queen.cpp
@@ -264,9 +264,7 @@ void QueenEngine::writeOptionSettings() {
}
void QueenEngine::update(bool checkPlayerInput) {
- if (_debugger->isAttached()) {
- _debugger->onFrame();
- }
+ _debugger->onFrame();
_graphics->update(_logic->currentRoom());
_logic->update();
@@ -424,7 +422,7 @@ void QueenEngine::makeGameStateName(int slot, char *buf) const {
int QueenEngine::getGameStateSlot(const char *filename) const {
int i = -1;
const char *slot = strrchr(filename, '.');
- if (slot && slot[1] == 's') {
+ if (slot && (slot[1] == 's' || slot[1] == 'S')) {
i = atoi(slot + 2);
}
return i;
diff --git a/engines/queen/resource.cpp b/engines/queen/resource.cpp
index a43141ef56..1e2eff8458 100644
--- a/engines/queen/resource.cpp
+++ b/engines/queen/resource.cpp
@@ -96,18 +96,7 @@ ResourceEntry *Resource::resourceEntry(const char *filename) const {
entryName.toUppercase();
ResourceEntry *re = NULL;
-#ifndef PALMOS_MODE
re = (ResourceEntry *)bsearch(entryName.c_str(), _resourceTable, _resourceEntries, sizeof(ResourceEntry), compareResourceEntry);
-#else
- // PALMOS FIXME (?) : still doesn't work for me (????) use this instead
- uint32 cur = 0;
- do {
- if (!strcmp(entryName.c_str(), _resourceTable[cur].filename)) {
- re = &_resourceTable[cur];
- break;
- }
- } while (++cur < _resourceEntries);
-#endif
return re;
}
diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
index c3f5fec83a..8bc8025032 100644
--- a/engines/saga/actor.cpp
+++ b/engines/saga/actor.cpp
@@ -1173,7 +1173,9 @@ void Actor::drawActors() {
return;
}
- if (_vm->_anim->hasCutaway()) {
+ // WORKAROUND
+ // Bug #2928923: 'ITE: Graphic Glitches during racoon death "Cut Scene"'
+ if (_vm->_anim->hasCutaway() || _vm->_scene->currentSceneNumber() == 287 || _vm->_scene->currentSceneNumber() == 286) {
drawSpeech();
return;
}
diff --git a/engines/saga/console.cpp b/engines/saga/console.cpp
index 2c201ac57d..228febfe9c 100644
--- a/engines/saga/console.cpp
+++ b/engines/saga/console.cpp
@@ -40,9 +40,6 @@ Console::Console(SagaEngine *vm) : GUI::Debugger() {
DCmd_Register("continue", WRAP_METHOD(Console, Cmd_Exit));
- // 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", WRAP_METHOD(Console, cmdActorWalkTo));
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index 1c2c6bacff..7913291527 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -122,7 +122,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class SagaMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp
index d15f0b2af3..1f4091d07c 100644
--- a/engines/saga/events.cpp
+++ b/engines/saga/events.cpp
@@ -306,7 +306,6 @@ int Events::handleOneShot(Event *event) {
_vm->_sndRes->playVoice(event->param);
break;
case kMusicEvent:
- _vm->_music->stop();
if (event->op == kEventPlay)
_vm->_music->play(event->param, (MusicFlags)event->param2);
break;
diff --git a/engines/saga/font.h b/engines/saga/font.h
index f290384e87..d8b1da30b9 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -190,7 +190,7 @@ class Font {
}
}
bool valid(FontId fontId) {
- return ((fontId >= 0) && (fontId < _loadedFonts));
+ return (fontId < _loadedFonts);
}
int getByteLen(int numBits) const {
int byteLength = numBits / 8;
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index c0d3cee465..c4b4688785 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -544,8 +544,10 @@ bool Interface::processAscii(Common::KeyState keystate) {
return true;
}
+#ifdef ENABLE_IHNM
if (_vm->_scene->isNonInteractiveIHNMDemoPart())
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
case kPanelCutaway:
if (keystate.keycode == Common::KEYCODE_ESCAPE) {
@@ -555,8 +557,10 @@ bool Interface::processAscii(Common::KeyState keystate) {
return true;
}
+#ifdef ENABLE_INHM
if (_vm->_scene->isNonInteractiveIHNMDemoPart())
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
case kPanelVideo:
if (keystate.keycode == Common::KEYCODE_ESCAPE) {
@@ -570,8 +574,10 @@ bool Interface::processAscii(Common::KeyState keystate) {
return true;
}
+#ifdef ENABLE_IHNM
if (_vm->_scene->isNonInteractiveIHNMDemoPart())
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
case kPanelOption:
// TODO: check input dialog keys
@@ -1855,6 +1861,7 @@ void Interface::update(const Point& mousePoint, int updateFlag) {
if (updateFlag & UPDATE_MOUSECLICK) {
if (!_vm->isIHNMDemo()) {
_vm->_scene->clearPsychicProfile();
+ _vm->_script->wakeUpThreads(kWaitTypeDelay);
} else {
setMode(kPanelConverse);
_vm->_scene->_textList.clear();
@@ -1866,8 +1873,10 @@ void Interface::update(const Point& mousePoint, int updateFlag) {
break;
case kPanelNull:
+#ifdef ENABLE_IHNM
if (_vm->_scene->isNonInteractiveIHNMDemoPart() && (updateFlag & UPDATE_MOUSECLICK))
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
}
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index 75c5cdffd7..e4a16e27da 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -42,19 +42,25 @@ namespace Saga {
#define BUFFER_SIZE 4096
#define MUSIC_SUNSPOT 26
-MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) {
+MusicDriver::MusicDriver() : _isGM(false) {
memset(_channel, 0, sizeof(_channel));
_masterVolume = 0;
+ _nativeMT32 = ConfMan.getBool("native_mt32");
+
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = MidiDriver::createMidi(dev);
+ if (isMT32())
+ _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+
this->open();
}
-MusicPlayer::~MusicPlayer() {
- _driver->setTimerCallback(NULL, NULL);
- stopMusic();
+MusicDriver::~MusicDriver() {
this->close();
+ delete _driver;
}
-void MusicPlayer::setVolume(int volume) {
+void MusicDriver::setVolume(int volume) {
volume = CLIP(volume, 0, 255);
if (_masterVolume == volume)
@@ -71,32 +77,7 @@ void MusicPlayer::setVolume(int volume) {
}
}
-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;
- }
-
+void MusicDriver::send(uint32 b) {
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume
@@ -104,71 +85,88 @@ void MusicPlayer::send(uint32 b) {
_channelVolume[channel] = volume;
volume = volume * _masterVolume / 255;
b = (b & 0xFF00FFFF) | (volume << 16);
- } else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+ } else if ((b & 0xF0) == 0xC0 && !_isGM && !isMT32()) {
+ // Remap MT32 instruments to General Midi
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])
+ } else if ((b & 0xFFF0) == 0x007BB0) {
+ // Only respond to All Notes Off if this channel
+ // has currently been allocated
+ if (!_channel[channel])
return;
}
if (!_channel[channel])
_channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
-
- if (_channel[channel])
+ else
_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;
- Common::StackLock lock(music->_mutex);
-
- if (music->_isPlaying)
- music->_parser->onTimer();
-}
-
-void MusicPlayer::playMusic() {
- _isPlaying = true;
-}
+Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
+ _currentVolume = 0;
+ _driver = new MusicDriver();
-void MusicPlayer::stopMusic() {
- Common::StackLock lock(_mutex);
+ _digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE);
+ if (!_driver->isAdlib())
+ _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_GM);
+ else
+ _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_FM);
- _isPlaying = false;
- if (_parser) {
- _parser->unloadMusic();
- _parser = NULL;
+ if (!_musicContext) {
+ if (_vm->getGameId() == GID_ITE) {
+ _musicContext = _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.
+ // Note that the IHNM demo has only got one music file
+ // (music.rsc). It is assumed that it contains FM music
+ _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_FM);
+ }
}
-}
-Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver) : _vm(vm), _mixer(mixer), _adlib(false) {
- _player = new MusicPlayer(driver);
- _currentVolume = 0;
-
- xmidiParser = MidiParser::createParser_XMIDI();
- smfParser = MidiParser::createParser_SMF();
+ // Check if the game is using XMIDI or SMF music
+ if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) {
+ // Just set an XMIDI parser for Mac IHNM for now
+ _parser = MidiParser::createParser_XMIDI();
+ } else {
+ byte *resourceData;
+ size_t resourceSize;
+ int resourceId = (_vm->getGameId() == GID_ITE ? 9 : 0);
+ _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
+ if (!memcmp(resourceData, "FORM", 4)) {
+ _parser = MidiParser::createParser_XMIDI();
+ // ITE had MT32 mapped instruments
+ _driver->setGM(_vm->getGameId() != GID_ITE);
+ } else {
+ _parser = MidiParser::createParser_SMF();
+ // ITE with standalone MIDI files is General MIDI
+ _driver->setGM(_vm->getGameId() == GID_ITE);
+ }
+ free(resourceData);
+ }
- _digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE);
+ _parser->setMidiDriver(_driver);
+ _parser->setTimerRate(_driver->getBaseTempo());
+ _parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
_songTableLen = 0;
_songTable = 0;
@@ -180,11 +178,10 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver) : _vm(vm),
Music::~Music() {
_vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback);
_mixer->stopHandle(_musicHandle);
- delete _player;
- xmidiParser->setMidiDriver(NULL);
- smfParser->setMidiDriver(NULL);
- delete xmidiParser;
- delete smfParser;
+ _driver->setTimerCallback(NULL, NULL);
+ delete _driver;
+ _parser->setMidiDriver(NULL);
+ delete _parser;
free(_songTable);
free(_midiMusicData);
@@ -194,6 +191,12 @@ void Music::musicVolumeGaugeCallback(void *refCon) {
((Music *)refCon)->musicVolumeGauge();
}
+void Music::onTimer(void *refCon) {
+ Music *music = (Music *)refCon;
+ Common::StackLock lock(music->_driver->_mutex);
+ music->_parser->onTimer();
+}
+
void Music::musicVolumeGauge() {
int volume;
@@ -209,7 +212,7 @@ void Music::musicVolumeGauge() {
volume = 1;
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
- _player->setVolume(volume);
+ _driver->setVolume(volume);
if (_currentVolumePercent == 100) {
_vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback);
@@ -226,23 +229,21 @@ void Music::setVolume(int volume, int time) {
if (time == 1) {
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
- _player->setVolume(volume);
+ _driver->setVolume(volume);
_vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback);
_currentVolume = volume;
return;
}
- _vm->getTimerManager()->installTimerProc(&musicVolumeGaugeCallback, time * 100L, this);
+ _vm->getTimerManager()->installTimerProc(&musicVolumeGaugeCallback, time * 3000L, this);
}
bool Music::isPlaying() {
- return _mixer->isSoundHandleActive(_musicHandle) || _player->isPlaying();
+ return _mixer->isSoundHandleActive(_musicHandle) || _parser->isPlaying();
}
void Music::play(uint32 resourceId, MusicFlags flags) {
Audio::SeekableAudioStream *audioStream = NULL;
- MidiParser *parser;
- ResourceContext *context = NULL;
byte *resourceData;
size_t resourceSize;
uint32 loopStart;
@@ -254,8 +255,8 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
}
_trackNumber = resourceId;
- _player->stopMusic();
_mixer->stopHandle(_musicHandle);
+ _parser->unloadMusic();
int realTrackNumber;
@@ -356,55 +357,10 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
return;
}
- if (flags == MUSIC_DEFAULT) {
+ if (flags == MUSIC_DEFAULT)
flags = MUSIC_NORMAL;
- }
// Load MIDI/XMI resource data
-
- if (_vm->getGameId() == GID_ITE) {
- context = _vm->_resource->getContext(GAME_MUSICFILE_GM);
- if (context == NULL) {
- context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- }
- } else if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) {
- // The music of the Mac version of IHNM is loaded from its
- // associated external file later on
- } 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.
- // Note that the IHNM demo has only got one music file
- // (music.rsc). It is assumed that it contains FM music
-
- if (hasAdLib() || _vm->isIHNMDemo()) {
- context = _vm->_resource->getContext(GAME_MUSICFILE_FM);
- } else {
- context = _vm->_resource->getContext(GAME_MUSICFILE_GM);
- }
- }
-
- _player->setGM(true);
-
if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) {
// Load the external music file for Mac IHNM
#if 0
@@ -422,56 +378,39 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
#endif
return;
} else {
- _vm->_resource->loadResource(context, resourceId, resourceData, resourceSize);
+ _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
}
if (resourceSize < 4) {
error("Music::play() wrong music resource size");
}
- if (xmidiParser->loadMusic(resourceData, resourceSize)) {
- if (_vm->getGameId() == GID_ITE)
- _player->setGM(false);
+ if (!_parser->loadMusic(resourceData, resourceSize))
+ error("Music::play() wrong music resource");
- 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);
+ _parser->setTrack(0);
+ _driver->setTimerCallback(this, &onTimer);
- _player->_parser = parser;
setVolume(_vm->_musicVolume);
- if (flags & MUSIC_LOOP)
- _player->setLoop(true);
- else
- _player->setLoop(false);
+ // Handle music looping
+ _parser->property(MidiParser::mpAutoLoop, (flags & MUSIC_LOOP) ? 1 : 0);
- _player->playMusic();
free(_midiMusicData);
_midiMusicData = resourceData;
}
void Music::pause() {
- _player->setVolume(-1);
- _player->setPlaying(false);
+ _driver->setTimerCallback(NULL, NULL);
}
void Music::resume() {
- _player->setVolume(_vm->_musicVolume);
- _player->setPlaying(true);
+ _driver->setTimerCallback(this, &onTimer);
}
void Music::stop() {
- _player->stopMusic();
+ _driver->setTimerCallback(NULL, NULL);
+ _parser->unloadMusic();
}
} // End of namespace Saga
diff --git a/engines/saga/music.h b/engines/saga/music.h
index 22711ba167..5cce3d4c04 100644
--- a/engines/saga/music.h
+++ b/engines/saga/music.h
@@ -44,41 +44,32 @@ enum MusicFlags {
MUSIC_DEFAULT = 0xffff
};
-class MusicPlayer : public MidiDriver {
+class MusicDriver : public MidiDriver {
public:
- MusicPlayer(MidiDriver *driver);
- ~MusicPlayer();
-
- bool isPlaying() { return _isPlaying; }
- void setPlaying(bool playing) { _isPlaying = playing; }
+ MusicDriver();
+ ~MusicDriver();
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; }
-
+ bool isAdlib() { return _driverType == MT_ADLIB; }
+ bool isMT32() { return _driverType == MT_MT32 || _nativeMT32; }
void setGM(bool isGM) { _isGM = isGM; }
//MidiDriver interface implementation
- int open();
- void close();
+ int open() { return _driver->open(); }
+ void close() { _driver->close(); }
void send(uint32 b);
- void metaEvent(byte type, byte *data, uint16 length);
+ void metaEvent(byte type, byte *data, uint16 length) {}
- void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
- uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; }
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { _driver->setTimerCallback(timerParam, timerProc); }
+ uint32 getBaseTempo() { return _driver->getBaseTempo(); }
//Channel allocation functions
MidiChannel *allocateChannel() { return 0; }
MidiChannel *getPercussionChannel() { return 0; }
- MidiParser *_parser;
Common::Mutex _mutex;
protected:
@@ -87,14 +78,11 @@ protected:
MidiChannel *_channel[16];
MidiDriver *_driver;
+ MusicType _driverType;
byte _channelVolume[16];
- bool _nativeMT32;
bool _isGM;
- bool _passThrough;
+ bool _nativeMT32;
- bool _isPlaying;
- bool _looping;
- bool _randomLoop;
byte _masterVolume;
byte *_musicData;
@@ -105,13 +93,8 @@ protected:
class Music {
public:
- Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver);
+ Music(SagaEngine *vm, Audio::Mixer *mixer);
~Music();
- 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();
bool hasDigitalMusic() { return _digitalMusic; }
@@ -130,24 +113,23 @@ private:
SagaEngine *_vm;
Audio::Mixer *_mixer;
- MusicPlayer *_player;
+ MusicDriver *_driver;
Audio::SoundHandle _musicHandle;
uint32 _trackNumber;
- bool _adlib;
-
int _targetVolume;
int _currentVolume;
int _currentVolumePercent;
bool _digitalMusic;
+ ResourceContext *_musicContext;
ResourceContext *_digitalMusicContext;
- MidiParser *xmidiParser;
- MidiParser *smfParser;
+ MidiParser *_parser;
byte *_midiMusicData;
static void musicVolumeGaugeCallback(void *refCon);
+ static void onTimer(void *refCon);
void musicVolumeGauge();
};
diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp
index 957ab3c8b6..5b13473d77 100644
--- a/engines/saga/puzzle.cpp
+++ b/engines/saga/puzzle.cpp
@@ -411,12 +411,12 @@ void Puzzle::solicitHint() {
switch (_hintRqState) {
case kRQSpeaking:
if (_vm->_actor->isSpeaking()) {
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50 * 1000000, this);
break;
}
_hintRqState = _hintNextRqState;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 333333, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 100*1000000/3, this);
break;
case kRQNoHint:
@@ -439,11 +439,11 @@ void Puzzle::solicitHint() {
// Roll to see if Sakka scolds
if (_vm->_rnd.getRandomNumber(1)) {
_hintRqState = kRQSakkaDenies;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 200000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 200*1000000, this);
} else {
_hintRqState = kRQSpeaking;
_hintNextRqState = kRQHintRequested;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50*1000000, this);
}
break;
@@ -456,7 +456,7 @@ void Puzzle::solicitHint() {
_hintRqState = kRQSpeaking;
_hintNextRqState = kRQHintRequestedStage2;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50*1000000, this);
_vm->_interface->converseClear();
_vm->_interface->converseAddText(optionsStr[_lang][kROAccept], 0, 1, 0, 0 );
diff --git a/engines/saga/render.cpp b/engines/saga/render.cpp
index ec168d296e..dc9334b037 100644
--- a/engines/saga/render.cpp
+++ b/engines/saga/render.cpp
@@ -110,6 +110,12 @@ void Render::drawScene() {
_vm->_actor->drawActors();
}
+ // WORKAROUND
+ // Bug #2886130: "ITE: Graphic Glitches during Cat Tribe Celebration"
+ if (_vm->_scene->currentSceneNumber() == 274) {
+ _vm->_interface->drawStatusBar();
+ }
+
#ifdef SAGA_DEBUG
if (getFlags() & RF_OBJECTMAP_TEST) {
if (_vm->_scene->_objectMap)
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 2911d9c451..025462c558 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -83,7 +83,6 @@ SagaEngine::SagaEngine(OSystem *syst, const SAGAGameDescription *gameDesc)
_sndRes = NULL;
_sound = NULL;
_music = NULL;
- _driver = NULL;
_anim = NULL;
_render = NULL;
_isoMap = NULL;
@@ -198,9 +197,6 @@ SagaEngine::~SagaEngine() {
delete _sound;
_sound = NULL;
- delete _driver;
- _driver = NULL;
-
delete _gfx;
_gfx = NULL;
@@ -285,17 +281,7 @@ Common::Error SagaEngine::run() {
_console = new Console(this);
// Graphics should be initialized before music
- MidiDriverType 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);
-
- _driver = MidiDriver::createMidi(midiDriver);
- if (native_mt32)
- _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
-
- _music = new Music(this, _mixer, _driver);
- _music->setNativeMT32(native_mt32);
- _music->setAdLib(adlib);
+ _music = new Music(this, _mixer);
_render = new Render(this, _system);
if (!_render->initialized()) {
return Common::kUnknownError;
@@ -380,8 +366,7 @@ Common::Error SagaEngine::run() {
uint32 currentTicks;
while (!shouldQuit()) {
- if (_console->isAttached())
- _console->onFrame();
+ _console->onFrame();
if (_render->getFlags() & RF_RENDERPAUSE) {
// Freeze time while paused
@@ -447,7 +432,7 @@ void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPoin
offset = scriptS.readUint16();
// In some rooms in IHNM, string offsets can be greater than the maximum value than a 16-bit integer can hold
// We detect this by checking the previous offset, and if it was bigger than the current one, an overflow
- // occured (since the string offsets are sequential), so we're adding the missing part of the number
+ // occurred (since the string offsets are sequential), so we're adding the missing part of the number
// Fixes bug #1895205 - "IHNM: end game text/caption error"
if (prevOffset > offset)
offset += 65536;
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
index 2bef489e05..102d1e5c82 100644
--- a/engines/saga/saga.h
+++ b/engines/saga/saga.h
@@ -525,7 +525,6 @@ public:
SndRes *_sndRes;
Sound *_sound;
Music *_music;
- MidiDriver *_driver;
Anim *_anim;
Render *_render;
IsoMap *_isoMap;
diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp
index 40ee1fb720..d7ee037c50 100644
--- a/engines/saga/scene.cpp
+++ b/engines/saga/scene.cpp
@@ -1201,6 +1201,12 @@ void Scene::endScene() {
_vm->_script->abortAllThreads();
_vm->_script->_skipSpeeches = false;
+ // WORKAROUND: Bug #2886151: "ITE: Mouse stops responding at Boar Castle"
+ // This is bug in original engine
+ if (_sceneNumber == 50) {
+ _vm->_interface->activate();
+ }
+
// Copy current screen to render buffer so inset rooms will get proper background
if (!(_sceneDescription.flags & kSceneFlagISO) && !_vm->_scene->isInIntro()) {
BGInfo bgInfo;
@@ -1425,6 +1431,8 @@ void Scene::clearPlacard() {
q_event = _vm->_events->chain(q_event, &event);
}
+#ifdef ENABLE_IHNM
+
void Scene::showPsychicProfile(const char *text) {
int textHeight;
static PalEntry cur_pal[PAL_ENTRIES];
@@ -1440,6 +1448,8 @@ void Scene::showPsychicProfile(const char *text) {
_vm->_interface->setMode(kPanelPlacard);
_vm->_gfx->savePalette();
+ _vm->_events->clearList();
+
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
@@ -1531,4 +1541,6 @@ void Scene::showIHNMDemoSpecialScreen() {
_vm->_scene->changeScene(150, 0, kTransitionFade);
}
+#endif // IHNM
+
} // End of namespace Saga
diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp
index 18bbca2425..5fd120ac33 100644
--- a/engines/saga/script.cpp
+++ b/engines/saga/script.cpp
@@ -571,9 +571,7 @@ void Script::opCall(SCRIPTOP_PARAMS) {
if (iparam1 != kAddressModule) {
error("Script::runThread iparam1 != kAddressModule");
}
- byte *addr = thread->baseAddress(iparam1);
iparam1 = scriptS->readSint16LE();
- addr += iparam1;
thread->push(argumentsCount);
// NOTE: The original pushes the program
diff --git a/engines/saga/script.h b/engines/saga/script.h
index f31af7b2ea..21afeb5c44 100644
--- a/engines/saga/script.h
+++ b/engines/saga/script.h
@@ -246,16 +246,19 @@ public:
}
void waitWalk(void *threadObj) {
+ debug(3, "waitWalk()");
wait(kWaitTypeWalk);
_threadObj = threadObj;
}
void waitDelay(int sleepTime) {
+ debug(3, "waitDelay(%d)", sleepTime);
wait(kWaitTypeDelay);
_sleepTime = sleepTime;
}
void waitFrames(int frames) {
+ debug(3, "waitFrames(%d)", frames);
wait(kWaitTypeWaitFrames);
_frameWait = frames;
}
diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp
index f98a80acd8..328d4040af 100644
--- a/engines/saga/sfuncs.cpp
+++ b/engines/saga/sfuncs.cpp
@@ -370,12 +370,15 @@ void Script::sfStopBgdAnim(SCRIPTFUNC_PARAMS) {
// reenabled.
// Param1: boolean
void Script::sfLockUser(SCRIPTFUNC_PARAMS) {
- if (thread->pop()) {
+ int16 param = thread->pop();
+
+ if (param != 0) {
_vm->_interface->deactivate();
} else {
_vm->_interface->activate();
}
+ debug(1, "sfLockUser(%d)", param);
}
// Script function #12 (0x0C)
@@ -1153,18 +1156,6 @@ void Script::sfPlacardOff(SCRIPTFUNC_PARAMS) {
_vm->_scene->clearPlacard();
}
-void Script::sfPsychicProfile(SCRIPTFUNC_PARAMS) {
- thread->wait(kWaitTypePlacard);
-
- _vm->_scene->showPsychicProfile(thread->_strings->getString(thread->pop()));
-}
-
-void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) {
- // This is called a while after the psychic profile is
- // opened, to close it automatically
- _vm->_scene->clearPsychicProfile();
-}
-
// Script function #50 (0x32)
void Script::sfSetProtagState(SCRIPTFUNC_PARAMS) {
_vm->_actor->setProtagState(thread->pop());
@@ -1473,6 +1464,8 @@ void Script::sfPlayLoopedSound(SCRIPTFUNC_PARAMS) {
} else {
_vm->_sound->stopSound();
}
+
+ debug(1, "sfPlayLoopedSound(%d)", param);
}
// Script function #72 (0x48)
diff --git a/engines/saga/sfuncs_ihnm.cpp b/engines/saga/sfuncs_ihnm.cpp
index fe586b54ae..b98c1cb852 100644
--- a/engines/saga/sfuncs_ihnm.cpp
+++ b/engines/saga/sfuncs_ihnm.cpp
@@ -440,6 +440,18 @@ void Script::sfDisableAbortSpeeches(SCRIPTFUNC_PARAMS) {
_vm->_interface->disableAbortSpeeches(thread->pop() != 0);
}
+void Script::sfPsychicProfile(SCRIPTFUNC_PARAMS) {
+ thread->wait(kWaitTypePlacard);
+
+ _vm->_scene->showPsychicProfile(thread->_strings->getString(thread->pop()));
+}
+
+void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) {
+ // This is called a while after the psychic profile is
+ // opened, to close it automatically
+ _vm->_scene->clearPsychicProfile();
+}
+
} // End of namespace Saga
#endif
diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp
index 8ea3fc4003..a27608dcf5 100644
--- a/engines/saga/sndres.cpp
+++ b/engines/saga/sndres.cpp
@@ -159,7 +159,7 @@ void SndRes::playSound(uint32 resourceId, int volume, bool loop) {
return;
}
- _vm->_sound->playSound(buffer, volume, loop);
+ _vm->_sound->playSound(buffer, volume, loop, resourceId);
}
void SndRes::playVoice(uint32 resourceId) {
diff --git a/engines/saga/sound.cpp b/engines/saga/sound.cpp
index 811ee709f7..db979e8104 100644
--- a/engines/saga/sound.cpp
+++ b/engines/saga/sound.cpp
@@ -105,10 +105,20 @@ void Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundBuffer &buffe
_mixer->playStream(soundType, handle, Audio::makeLoopingAudioStream(stream, loop ? 0 : 1), -1, volume);
}
-void Sound::playSound(SoundBuffer &buffer, int volume, bool loop) {
+void Sound::playSound(SoundBuffer &buffer, int volume, bool loop, int resId) {
+ // WORKAROUND
+ // Prevent playing same looped sound for several times
+ // Fixes bug #2886141: "ITE: Cumulative Snoring sounds in Prince's Bedroom"
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kEffectHandle && _handles[i].resId == resId) {
+ debug(1, "Skipped playing SFX #%d", resId);
+ return;
+ }
+
SndHandle *handle = getHandle();
handle->type = kEffectHandle;
+ handle->resId = resId;
playSoundBuffer(&handle->handle, buffer, 2 * volume, handle->type, loop);
}
@@ -129,6 +139,7 @@ void Sound::stopSound() {
if (_handles[i].type == kEffectHandle) {
_mixer->stopHandle(_handles[i].handle);
_handles[i].type = kFreeHandle;
+ _handles[i].resId = -1;
}
}
diff --git a/engines/saga/sound.h b/engines/saga/sound.h
index 9cf8f29767..7ee2765a0f 100644
--- a/engines/saga/sound.h
+++ b/engines/saga/sound.h
@@ -63,6 +63,7 @@ enum sndHandleType {
struct SndHandle {
Audio::SoundHandle handle;
sndHandleType type;
+ int resId;
};
class Sound {
@@ -71,7 +72,7 @@ public:
Sound(SagaEngine *vm, Audio::Mixer *mixer);
~Sound();
- void playSound(SoundBuffer &buffer, int volume, bool loop);
+ void playSound(SoundBuffer &buffer, int volume, bool loop, int resId);
void pauseSound();
void resumeSound();
void stopSound();
diff --git a/engines/saga/sthread.cpp b/engines/saga/sthread.cpp
index c133f8de88..be674e5acd 100644
--- a/engines/saga/sthread.cpp
+++ b/engines/saga/sthread.cpp
@@ -58,6 +58,8 @@ ScriptThread &Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntry
_threadList.push_front(newThread);
+ debug(3, "createThread(). Total threads: %d", _threadList.size());
+
ScriptThread &tmp = *_threadList.begin();
tmp._stackBuf = (int16 *)malloc(ScriptThread::THREAD_STACK_SIZE * sizeof(int16));
tmp._stackTopIndex = ScriptThread::THREAD_STACK_SIZE - 2;
@@ -78,6 +80,8 @@ void Script::wakeUpActorThread(int waitType, void *threadObj) {
void Script::wakeUpThreads(int waitType) {
ScriptThreadList::iterator threadIterator;
+ debug(3, "wakeUpThreads(%d)", waitType);
+
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
ScriptThread &thread = *threadIterator;
if ((thread._flags & kTFlagWaiting) && (thread._waitType == waitType)) {
@@ -89,6 +93,8 @@ void Script::wakeUpThreads(int waitType) {
void Script::wakeUpThreadsDelayed(int waitType, int sleepTime) {
ScriptThreadList::iterator threadIterator;
+ debug(3, "wakeUpThreads(%d, %d)", waitType, sleepTime);
+
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
ScriptThread &thread = *threadIterator;
if ((thread._flags & kTFlagWaiting) && (thread._waitType == waitType)) {
@@ -169,6 +175,8 @@ void Script::executeThreads(uint msec) {
void Script::abortAllThreads() {
ScriptThreadList::iterator threadIterator;
+ debug(3, "abortAllThreads()");
+
threadIterator = _threadList.begin();
while (threadIterator != _threadList.end()) {
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 16bc69115f..fac819167b 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -31,22 +31,19 @@
#include "sci/event.h"
#include "sci/resource.h"
#include "sci/engine/state.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
#include "sci/engine/savegame.h"
#include "sci/engine/gc.h"
#include "sci/engine/features.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/songlib.h" // for SongLibrary
-#include "sci/sound/iterator/iterator.h" // for SCI_SONG_ITERATOR_TYPE_SCI0
-#else
+#include "sci/sound/midiparser_sci.h"
#include "sci/sound/music.h"
-#endif
#include "sci/sound/drivers/mididriver.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint.h"
+#include "sci/graphics/paint16.h"
+#include "sci/graphics/paint32.h"
#include "sci/graphics/palette.h"
#include "sci/parser/vocabulary.h"
@@ -54,7 +51,7 @@
#include "graphics/video/avi_decoder.h"
#include "sci/video/seq_decoder.h"
#ifdef ENABLE_SCI32
-#include "sci/video/vmd_decoder.h"
+#include "graphics/video/coktel_decoder.h"
#endif
#include "common/file.h"
@@ -69,15 +66,15 @@ bool g_debug_track_mouse_clicks = false;
// Refer to the "addresses" command on how to pass address parameters
static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeValue);
-Console::Console(SciEngine *engine) : GUI::Debugger() {
- _engine = engine;
+Console::Console(SciEngine *engine) : GUI::Debugger(),
+ _engine(engine), _debugState(engine->_debugState), _enterTime(0) {
// Variables
DVar_Register("sleeptime_factor", &g_debug_sleeptime_factor, DVAR_INT, 0);
- DVar_Register("gc_interval", &script_gc_interval, DVAR_INT, 0);
+ DVar_Register("gc_interval", &engine->_gamestate->scriptGCInterval, DVAR_INT, 0);
DVar_Register("simulated_key", &g_debug_simulated_key, DVAR_INT, 0);
DVar_Register("track_mouse_clicks", &g_debug_track_mouse_clicks, DVAR_BOOL, 0);
- DVar_Register("script_abort_flag", &script_abort_flag, DVAR_INT, 0);
+ DVar_Register("script_abort_flag", &_engine->_gamestate->abortScriptProcessing, DVAR_INT, 0);
// General
DCmd_Register("help", WRAP_METHOD(Console, cmdHelp));
@@ -96,6 +93,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
DCmd_Register("sentence_fragments", WRAP_METHOD(Console, cmdSentenceFragments));
DCmd_Register("parse", WRAP_METHOD(Console, cmdParse));
DCmd_Register("set_parse_nodes", WRAP_METHOD(Console, cmdSetParseNodes));
+ DCmd_Register("said", WRAP_METHOD(Console, cmdSaid));
// Resources
DCmd_Register("diskdump", WRAP_METHOD(Console, cmdDiskDump));
DCmd_Register("hexdump", WRAP_METHOD(Console, cmdHexDump));
@@ -104,6 +102,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
DCmd_Register("resource_types", WRAP_METHOD(Console, cmdResourceTypes));
DCmd_Register("list", WRAP_METHOD(Console, cmdList));
DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep));
+ DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts));
+ DCmd_Register("show_instruments", WRAP_METHOD(Console, cmdShowInstruments));
// Game
DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame));
DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame));
@@ -149,15 +149,17 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
DCmd_Register("addresses", WRAP_METHOD(Console, cmdAddresses));
DCmd_Register("registers", WRAP_METHOD(Console, cmdRegisters));
DCmd_Register("dissect_script", WRAP_METHOD(Console, cmdDissectScript));
- DCmd_Register("set_acc", WRAP_METHOD(Console, cmdSetAccumulator));
DCmd_Register("backtrace", WRAP_METHOD(Console, cmdBacktrace));
DCmd_Register("bt", WRAP_METHOD(Console, cmdBacktrace)); // alias
- DCmd_Register("step", WRAP_METHOD(Console, cmdStep));
- DCmd_Register("s", WRAP_METHOD(Console, cmdStep)); // alias
+ DCmd_Register("trace", WRAP_METHOD(Console, cmdTrace));
+ DCmd_Register("t", WRAP_METHOD(Console, cmdTrace)); // alias
+ DCmd_Register("s", WRAP_METHOD(Console, cmdTrace)); // alias
+ DCmd_Register("stepover", WRAP_METHOD(Console, cmdStepOver));
+ DCmd_Register("p", WRAP_METHOD(Console, cmdStepOver)); // alias
+ DCmd_Register("step_ret", WRAP_METHOD(Console, cmdStepRet));
+ DCmd_Register("pret", WRAP_METHOD(Console, cmdStepRet)); // alias
DCmd_Register("step_event", WRAP_METHOD(Console, cmdStepEvent));
DCmd_Register("se", WRAP_METHOD(Console, cmdStepEvent)); // alias
- DCmd_Register("step_ret", WRAP_METHOD(Console, cmdStepRet));
- DCmd_Register("sret", WRAP_METHOD(Console, cmdStepRet)); // alias
DCmd_Register("step_global", WRAP_METHOD(Console, cmdStepGlobal));
DCmd_Register("sg", WRAP_METHOD(Console, cmdStepGlobal)); // alias
DCmd_Register("step_callk", WRAP_METHOD(Console, cmdStepCallk));
@@ -166,60 +168,60 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
DCmd_Register("disasm_addr", WRAP_METHOD(Console, cmdDisassembleAddress));
DCmd_Register("send", WRAP_METHOD(Console, cmdSend));
DCmd_Register("go", WRAP_METHOD(Console, cmdGo));
+ DCmd_Register("logkernel", WRAP_METHOD(Console, cmdLogKernel));
// Breakpoints
DCmd_Register("bp_list", WRAP_METHOD(Console, cmdBreakpointList));
DCmd_Register("bplist", WRAP_METHOD(Console, cmdBreakpointList)); // alias
+ DCmd_Register("bl", WRAP_METHOD(Console, cmdBreakpointList)); // alias
DCmd_Register("bp_del", WRAP_METHOD(Console, cmdBreakpointDelete));
DCmd_Register("bpdel", WRAP_METHOD(Console, cmdBreakpointDelete)); // alias
- DCmd_Register("bp_exec_method", WRAP_METHOD(Console, cmdBreakpointExecMethod));
- DCmd_Register("bpx", WRAP_METHOD(Console, cmdBreakpointExecMethod)); // alias
- DCmd_Register("bp_exec_function", WRAP_METHOD(Console, cmdBreakpointExecFunction));
- DCmd_Register("bpe", WRAP_METHOD(Console, cmdBreakpointExecFunction)); // alias
+ DCmd_Register("bc", WRAP_METHOD(Console, cmdBreakpointDelete)); // alias
+ DCmd_Register("bp_method", WRAP_METHOD(Console, cmdBreakpointMethod));
+ DCmd_Register("bpx", WRAP_METHOD(Console, cmdBreakpointMethod)); // alias
+ DCmd_Register("bp_kernel", WRAP_METHOD(Console, cmdBreakpointKernel));
+ DCmd_Register("bpk", WRAP_METHOD(Console, cmdBreakpointKernel)); // alias
+ DCmd_Register("bp_function", WRAP_METHOD(Console, cmdBreakpointFunction));
+ DCmd_Register("bpe", WRAP_METHOD(Console, cmdBreakpointFunction)); // alias
// VM
DCmd_Register("script_steps", WRAP_METHOD(Console, cmdScriptSteps));
DCmd_Register("vm_varlist", WRAP_METHOD(Console, cmdVMVarlist));
- DCmd_Register("vmvarlist", WRAP_METHOD(Console, cmdVMVarlist)); // alias
+ DCmd_Register("vmvarlist", WRAP_METHOD(Console, cmdVMVarlist)); // alias
+ DCmd_Register("vl", WRAP_METHOD(Console, cmdVMVarlist)); // alias
DCmd_Register("vm_vars", WRAP_METHOD(Console, cmdVMVars));
- DCmd_Register("vmvars", WRAP_METHOD(Console, cmdVMVars)); // alias
+ DCmd_Register("vmvars", WRAP_METHOD(Console, cmdVMVars)); // alias
+ DCmd_Register("vv", WRAP_METHOD(Console, cmdVMVars)); // alias
DCmd_Register("stack", WRAP_METHOD(Console, cmdStack));
DCmd_Register("value_type", WRAP_METHOD(Console, cmdValueType));
DCmd_Register("view_listnode", WRAP_METHOD(Console, cmdViewListNode));
DCmd_Register("view_reference", WRAP_METHOD(Console, cmdViewReference));
- DCmd_Register("vr", WRAP_METHOD(Console, cmdViewReference)); // alias
+ DCmd_Register("vr", WRAP_METHOD(Console, cmdViewReference)); // alias
DCmd_Register("view_object", WRAP_METHOD(Console, cmdViewObject));
- DCmd_Register("vo", WRAP_METHOD(Console, cmdViewObject)); // alias
+ DCmd_Register("vo", WRAP_METHOD(Console, cmdViewObject)); // alias
DCmd_Register("active_object", WRAP_METHOD(Console, cmdViewActiveObject));
DCmd_Register("acc_object", WRAP_METHOD(Console, cmdViewAccumulatorObject));
- g_debugState.seeking = kDebugSeekNothing;
- g_debugState.seekLevel = 0;
- g_debugState.runningStep = 0;
- g_debugState.stopOnEvent = false;
- g_debugState.debugging = false;
- g_debugState.breakpointWasHit = false;
- g_debugState._breakpoints.clear(); // No breakpoints defined
- g_debugState._activeBreakpointTypes = 0;
+ _debugState.seeking = kDebugSeekNothing;
+ _debugState.seekLevel = 0;
+ _debugState.runningStep = 0;
+ _debugState.stopOnEvent = false;
+ _debugState.debugging = false;
+ _debugState.breakpointWasHit = false;
+ _debugState._breakpoints.clear(); // No breakpoints defined
+ _debugState._activeBreakpointTypes = 0;
}
Console::~Console() {
}
void Console::preEnter() {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (_engine->_gamestate)
- _engine->_gamestate->_sound.sfx_suspend(true);
-#endif
- if (_engine->_gamestate && _engine->_gamestate->_soundCmd)
- _engine->_gamestate->_soundCmd->pauseAll(true);
+ if (g_sci && g_sci->_soundCmd)
+ g_sci->_soundCmd->pauseAll(true);
+ _enterTime = g_system->getMillis();
}
void Console::postEnter() {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (_engine->_gamestate)
- _engine->_gamestate->_sound.sfx_suspend(false);
-#endif
- if (_engine->_gamestate && _engine->_gamestate->_soundCmd)
- _engine->_gamestate->_soundCmd->pauseAll(false);
+ if (g_sci && g_sci->_soundCmd)
+ g_sci->_soundCmd->pauseAll(false);
if (!_videoFile.empty()) {
_engine->_gfxCursor->kernelHide();
@@ -232,7 +234,7 @@ void Console::postEnter() {
videoDecoder = seqDecoder;
#ifdef ENABLE_SCI32
} else if (_videoFile.hasSuffix(".vmd")) {
- videoDecoder = new VMDDecoder(g_system->getMixer());
+ videoDecoder = new Graphics::VMDDecoder(g_system->getMixer());
#endif
} else if (_videoFile.hasSuffix(".avi")) {
videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
@@ -241,8 +243,12 @@ void Console::postEnter() {
if (videoDecoder && videoDecoder->loadFile(_videoFile)) {
uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
+ bool skipVideo = false;
+
+ if (videoDecoder->hasDirtyPalette())
+ videoDecoder->setSystemPalette();
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {
+ while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
@@ -256,8 +262,10 @@ void Console::postEnter() {
}
Common::Event event;
- while (g_system->getEventManager()->pollEvent(event))
- ;
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+ skipVideo = true;
+ }
g_system->delayMillis(10);
}
@@ -270,15 +278,10 @@ void Console::postEnter() {
_videoFile.clear();
_videoFrameDelay = 0;
}
-}
-
-#if 0
-// Unused
-#define LOOKUP_SPECIES(species) (\
- (species >= 1000) ? species : *(s->_classtable[species].scriptposp) \
- + s->_classtable[species].class_offset)
-#endif
+ // Subtract the time we were running the debugger from the game running time
+ _engine->_gamestate->gameStartTime += g_system->getMillis() - _enterTime;
+}
bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf("\n");
@@ -314,6 +317,7 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" sentence_fragments - Shows the sentence fragments (used to build Parse trees)\n");
DebugPrintf(" parse - Parses a sequence of words and prints the resulting parse tree\n");
DebugPrintf(" set_parse_nodes - Sets the contents of all parse nodes\n");
+ DebugPrintf(" said - Match a string against a said spec\n");
DebugPrintf("\n");
DebugPrintf("Resources:\n");
DebugPrintf(" diskdump - Dumps the specified resource to disk as a patch file\n");
@@ -323,6 +327,8 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" resource_types - Shows the valid resource types\n");
DebugPrintf(" list - Lists all the resources of a given type\n");
DebugPrintf(" hexgrep - Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers\n");
+ DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n");
+ DebugPrintf(" show_instruments - Shows the instruments of a specific song, or all songs\n");
DebugPrintf("\n");
DebugPrintf("Game:\n");
DebugPrintf(" save_game - Saves the current game state to the hard disk\n");
@@ -367,28 +373,30 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" addresses - Provides information on how to pass addresses\n");
DebugPrintf(" registers - Shows the current register values\n");
DebugPrintf(" dissect_script - Examines a script\n");
- DebugPrintf(" set_acc - Sets the accumulator\n");
DebugPrintf(" backtrace / bt - Dumps the send/self/super/call/calle/callb stack\n");
- DebugPrintf(" step / s - Executes one operation (no parameters) or several operations (specified as a parameter) \n");
+ DebugPrintf(" trace / t / s - Executes one operation (no parameters) or several operations (specified as a parameter) \n");
+ DebugPrintf(" stepover / p - Executes one operation, skips over call/send\n");
+ DebugPrintf(" step_ret / pret - Steps forward until ret is called on the current execution stack level.\n");
DebugPrintf(" step_event / se - Steps forward until a SCI event is received.\n");
- DebugPrintf(" step_ret / sret - Steps forward until ret is called on the current execution stack level.\n");
DebugPrintf(" step_global / sg - Steps until the global variable with the specified index is modified.\n");
DebugPrintf(" step_callk / snk - Steps forward until it hits the next callk operation, or a specific callk (specified as a parameter)\n");
DebugPrintf(" disasm - Disassembles a method by name\n");
DebugPrintf(" disasm_addr - Disassembles one or more commands\n");
DebugPrintf(" send - Sends a message to an object\n");
DebugPrintf(" go - Executes the script\n");
+ DebugPrintf(" logkernel - Logs kernel calls\n");
DebugPrintf("\n");
DebugPrintf("Breakpoints:\n");
- DebugPrintf(" bp_list / bplist - Lists the current breakpoints\n");
- DebugPrintf(" bp_del / bpdel - Deletes a breakpoint with the specified index\n");
- DebugPrintf(" bp_exec_method / bpx - Sets a breakpoint on the execution of the specified method\n");
- DebugPrintf(" bp_exec_function / bpe - Sets a breakpoint on the execution of the specified exported function\n");
+ DebugPrintf(" bp_list / bplist / bl - Lists the current breakpoints\n");
+ DebugPrintf(" bp_del / bpdel / bc - Deletes a breakpoint with the specified index\n");
+ DebugPrintf(" bp_method / bpx - Sets a breakpoint on the execution or access of a specified method/selector\n");
+ DebugPrintf(" bp_kernel / bpk - Sets a breakpoint on execution of a kernel function\n");
+ DebugPrintf(" bp_function / bpe - Sets a breakpoint on the execution of the specified exported function\n");
DebugPrintf("\n");
DebugPrintf("VM:\n");
DebugPrintf(" script_steps - Shows the number of executed SCI operations\n");
- DebugPrintf(" vm_varlist / vmvarlist - Shows the addresses of variables in the VM\n");
- DebugPrintf(" vm_vars / vmvars - Displays or changes variables in the VM\n");
+ DebugPrintf(" vm_varlist / vmvarlist / vl - Shows the addresses of variables in the VM\n");
+ DebugPrintf(" vm_vars / vmvars / vv - Displays or changes variables in the VM\n");
DebugPrintf(" stack - Lists the specified number of stack elements\n");
DebugPrintf(" value_type - Determines the type of a value\n");
DebugPrintf(" view_listnode - Examines the list node at the given address\n");
@@ -411,20 +419,19 @@ ResourceType parseResourceType(const char *resid) {
return res;
}
-const char *selector_name(EngineState *s, int selector) {
- if (selector >= 0 && selector < (int)g_sci->getKernel()->getSelectorNamesSize())
- return g_sci->getKernel()->getSelectorName(selector).c_str();
- else
- return "--INVALID--";
-}
-
bool Console::cmdGetVersion(int argc, const char **argv) {
const char *viewTypeDesc[] = { "Unknown", "EGA", "VGA", "VGA SCI1.1", "Amiga" };
- EngineState *s = _engine->_gamestate;
bool hasVocab997 = g_sci->getResMan()->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS)) ? true : false;
+ Common::String gameVersion = "N/A";
- DebugPrintf("Game ID: %s\n", s->_gameId.c_str());
+ Common::File versionFile;
+ if (versionFile.open("VERSION")) {
+ gameVersion = versionFile.readLine();
+ versionFile.close();
+ }
+
+ DebugPrintf("Game ID: %s\n", _engine->getGameIdStr());
DebugPrintf("Emulated interpreter version: %s\n", getSciVersionDesc(getSciVersion()));
DebugPrintf("\n");
DebugPrintf("Detected features:\n");
@@ -432,12 +439,14 @@ bool Console::cmdGetVersion(int argc, const char **argv) {
DebugPrintf("Sound type: %s\n", getSciVersionDesc(_engine->_features->detectDoSoundType()));
DebugPrintf("Graphics functions type: %s\n", getSciVersionDesc(_engine->_features->detectGfxFunctionsType()));
DebugPrintf("Lofs type: %s\n", getSciVersionDesc(_engine->_features->detectLofsType()));
- DebugPrintf("Move count type: %s\n", (_engine->_features->detectMoveCountType() == kIncrementMoveCount) ? "increment" : "ignore");
+ DebugPrintf("Move count type: %s\n", (_engine->_features->handleMoveCount()) ? "increment" : "ignore");
DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(_engine->_features->detectSetCursorType()));
DebugPrintf("View type: %s\n", viewTypeDesc[g_sci->getResMan()->getViewType()]);
+ DebugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette->isMerging() ? "yes" : "no");
DebugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc());
DebugPrintf("Resource map version: %s\n", g_sci->getResMan()->getMapVersionDesc());
DebugPrintf("Contains selector vocabulary (vocab.997): %s\n", hasVocab997 ? "yes" : "no");
+ DebugPrintf("Game version (VERSION file): %s\n", gameVersion.c_str());
DebugPrintf("\n");
return true;
@@ -480,14 +489,14 @@ bool Console::cmdSelector(int argc, const char **argv) {
return true;
}
- for (uint seeker = 0; seeker < _engine->getKernel()->getSelectorNamesSize(); seeker++) {
- if (!scumm_stricmp(_engine->getKernel()->getSelectorName(seeker).c_str(), argv[1])) {
- DebugPrintf("Selector %s found at %03x (%d)\n", _engine->getKernel()->getSelectorName(seeker).c_str(), seeker, seeker);
- return true;
- }
+ Common::String name = argv[1];
+ int seeker = _engine->getKernel()->findSelector(name.c_str());
+ if (seeker >= 0) {
+ DebugPrintf("Selector %s found at %03x (%d)\n", name.c_str(), seeker, seeker);
+ return true;
}
- DebugPrintf("Selector %s wasn't found\n", argv[1]);
+ DebugPrintf("Selector %s wasn't found\n", name.c_str());
return true;
}
@@ -593,14 +602,14 @@ bool Console::cmdSetParseNodes(int argc, const char **argv) {
}
bool Console::cmdRegisters(int argc, const char **argv) {
+ EngineState *s = _engine->_gamestate;
DebugPrintf("Current register values:\n");
- DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(_engine->_gamestate->r_acc), PRINT_REG(_engine->_gamestate->r_prev), scriptState.restAdjust);
+ DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(s->r_acc), PRINT_REG(s->r_prev), s->restAdjust);
- if (!_engine->_gamestate->_executionStack.empty()) {
- EngineState *s = _engine->_gamestate; // for PRINT_STK
+ if (!s->_executionStack.empty()) {
DebugPrintf("pc=%04x:%04x obj=%04x:%04x fp=ST:%04x sp=ST:%04x\n",
- PRINT_REG(scriptState.xs->addr.pc), PRINT_REG(scriptState.xs->objp),
- (unsigned)(scriptState.xs->fp - s->stack_base), (unsigned)(scriptState.xs->sp - s->stack_base));
+ PRINT_REG(s->xs->addr.pc), PRINT_REG(s->xs->objp),
+ (unsigned)(s->xs->fp - s->stack_base), (unsigned)(s->xs->sp - s->stack_base));
} else
DebugPrintf("<no execution stack: pc,obj,fp omitted>\n");
@@ -811,6 +820,203 @@ bool Console::cmdHexgrep(int argc, const char **argv) {
return true;
}
+bool Console::cmdVerifyScripts(int argc, const char **argv) {
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ DebugPrintf("This script check is only meant for SCI1.1-SCI2.1 games\n");
+ return true;
+ }
+
+ Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript);
+ Common::sort(resources->begin(), resources->end());
+ Common::List<ResourceId>::iterator itr = resources->begin();
+
+ DebugPrintf("%d SCI1.1-SCI2.1 scripts found, performing sanity checks...\n", resources->size());
+
+ Resource *script, *heap;
+ while (itr != resources->end()) {
+ script = _engine->getResMan()->findResource(*itr, false);
+ if (!script)
+ DebugPrintf("Error: script %d couldn't be loaded\n", itr->getNumber());
+
+ heap = _engine->getResMan()->findResource(ResourceId(kResourceTypeHeap, itr->getNumber()), false);
+ if (!heap)
+ DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->getNumber());
+
+ if (script && heap && (script->size + heap->size > 65535))
+ DebugPrintf("Error: script and heap %d together are larger than 64KB (%d bytes)\n",
+ itr->getNumber(), script->size + heap->size);
+
+ ++itr;
+ }
+
+ DebugPrintf("SCI1.1-SCI2.1 script check finished\n");
+ delete resources;
+
+ return true;
+}
+
+bool Console::cmdShowInstruments(int argc, const char **argv) {
+ int songNumber = -1;
+
+ if (argc == 2)
+ songNumber = atoi(argv[1]);
+
+ SciVersion doSoundVersion = _engine->_features->detectDoSoundType();
+ MidiPlayer *player = MidiPlayer_Midi_create(doSoundVersion);
+ MidiParser_SCI *parser = new MidiParser_SCI(doSoundVersion, 0);
+ parser->setMidiDriver(player);
+
+ Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeSound);
+ Common::sort(resources->begin(), resources->end());
+ Common::List<ResourceId>::iterator itr = resources->begin();
+ int instruments[128];
+ bool instrumentsSongs[128][1000];
+
+ for (int i = 0; i < 128; i++)
+ instruments[i] = 0;
+
+ for (int i = 0; i < 128; i++)
+ for (int j = 0; j < 1000; j++)
+ instrumentsSongs[i][j] = false;
+
+ if (songNumber == -1) {
+ DebugPrintf("%d sounds found, checking their instrument mappings...\n", resources->size());
+ DebugPrintf("Instruments:\n");
+ DebugPrintf("============\n");
+ }
+
+ SoundResource *sound;
+
+ while (itr != resources->end()) {
+ if (songNumber >= 0 && itr->getNumber() != songNumber) {
+ ++itr;
+ continue;
+ }
+
+ sound = new SoundResource(itr->getNumber(), _engine->getResMan(), doSoundVersion);
+ int channelFilterMask = sound->getChannelFilterMask(player->getPlayId(), player->hasRhythmChannel());
+ SoundResource::Track *track = sound->getTrackByType(player->getPlayId());
+ if (track->digitalChannelNr != -1) {
+ // Skip digitized sound effects
+ delete sound;
+ ++itr;
+ continue;
+ }
+
+ parser->loadMusic(track, NULL, channelFilterMask, doSoundVersion);
+ const byte *channelData = parser->getMixedData();
+
+ byte curEvent = 0, prevEvent = 0, command = 0;
+ bool endOfTrack = false;
+ bool firstOneShown = false;
+
+ DebugPrintf("Song %d: ", itr->getNumber());
+
+ do {
+ while (*channelData == 0xF8)
+ channelData++;
+
+ channelData++; // delta
+
+ if ((*channelData & 0xF0) >= 0x80)
+ curEvent = *(channelData++);
+ else
+ curEvent = prevEvent;
+ if (curEvent < 0x80)
+ continue;
+
+ prevEvent = curEvent;
+ command = curEvent >> 4;
+
+ byte channel;
+
+ switch (command) {
+ case 0xC: // program change
+ channel = curEvent & 0x0F;
+ if (channel != 15) { // SCI special
+ byte instrument = *channelData++;
+ if (!firstOneShown)
+ firstOneShown = true;
+ else
+ DebugPrintf(",");
+
+ DebugPrintf(" %d", instrument);
+ instruments[instrument]++;
+ instrumentsSongs[instrument][itr->getNumber()] = true;
+ }
+ break;
+ case 0xD:
+ channelData++; // param1
+ break;
+ case 0xB:
+ channelData++; // param1
+ channelData++; // param2
+ break;
+ case 0x8:
+ case 0x9:
+ case 0xA:
+ case 0xE:
+ channelData++; // param1
+ channelData++; // param2
+ break;
+ case 0xF:
+ if ((curEvent & 0x0F) == 0x2) {
+ channelData++; // param1
+ channelData++; // param2
+ } else if ((curEvent & 0x0F) == 0x3) {
+ channelData++; // param1
+ } else if ((curEvent & 0x0F) == 0xF) { // META
+ byte type = *channelData++;
+ if (type == 0x2F) {// end of track reached
+ endOfTrack = true;
+ } else {
+ // no further processing necessary
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } while (!endOfTrack);
+
+ DebugPrintf("\n");
+
+ delete sound;
+ ++itr;
+ }
+
+ delete parser;
+ delete player;
+
+ DebugPrintf("\n");
+
+ if (songNumber == -1) {
+ DebugPrintf("Used instruments: ");
+ for (int i = 0; i < 128; i++) {
+ if (instruments[i] > 0)
+ DebugPrintf("%d, ", i);
+ }
+ DebugPrintf("\n\n");
+
+ DebugPrintf("Used instruments in songs:\n");
+ for (int i = 0; i < 128; i++) {
+ if (instruments[i] > 0) {
+ DebugPrintf("Instrument %d: ", i);
+ for (int j = 0; j < 1000; j++) {
+ if (instrumentsSongs[i][j])
+ DebugPrintf("%d, ", j);
+ }
+ DebugPrintf("\n");
+ }
+ }
+
+ DebugPrintf("\n\n");
+ }
+
+ delete resources;
+ return true;
+}
+
bool Console::cmdList(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Lists all the resources of a given type\n");
@@ -834,26 +1040,25 @@ bool Console::cmdList(int argc, const char **argv) {
}
Common::List<ResourceId> *resources = _engine->getResMan()->listResources(res, number);
- sort(resources->begin(), resources->end(), ResourceIdLess());
+ Common::sort(resources->begin(), resources->end());
Common::List<ResourceId>::iterator itr = resources->begin();
int cnt = 0;
while (itr != resources->end()) {
if (number == -1) {
- DebugPrintf("%8i", itr->number);
+ DebugPrintf("%8i", itr->getNumber());
if (++cnt % 10 == 0)
DebugPrintf("\n");
- }
- else if (number == (int)itr->number) {
- DebugPrintf("(%3i, %3i, %3i, %3i) ", (itr->tuple >> 24) & 0xff, (itr->tuple >> 16) & 0xff,
- (itr->tuple >> 8) & 0xff, itr->tuple & 0xff);
+ } else if (number == (int)itr->getNumber()) {
+ const uint32 tuple = itr->getTuple();
+ DebugPrintf("(%3i, %3i, %3i, %3i) ", (tuple >> 24) & 0xff, (tuple >> 16) & 0xff,
+ (tuple >> 8) & 0xff, tuple & 0xff);
if (++cnt % 4 == 0)
DebugPrintf("\n");
}
++itr;
}
DebugPrintf("\n");
-
delete resources;
}
@@ -884,7 +1089,7 @@ bool Console::cmdSaveGame(int argc, const char **argv) {
}
// TODO: enable custom descriptions? force filename into a specific format?
- if (gamestate_save(_engine->_gamestate, out, "debugging", version)) {
+ if (!gamestate_save(_engine->_gamestate, out, "debugging", version)) {
DebugPrintf("Saving the game state to '%s' failed\n", argv[1]);
} else {
out->finalize();
@@ -917,39 +1122,22 @@ bool Console::cmdRestoreGame(int argc, const char **argv) {
return true;
}
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdRestartGame(int argc, const char **argv) {
- if (argc != 2) {
- DebugPrintf("Restarts the game. There are two ways to restart a SCI game:\n");
- DebugPrintf("%s play - calls the game object's play() method\n", argv[0]);
- DebugPrintf("%s replay - calls the replay() methody\n", argv[0]);
- return true;
- }
-
- if (!scumm_stricmp(argv[1], "play")) {
- _engine->_gamestate->restarting_flags |= SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
- } else if (!scumm_stricmp(argv[1], "replay")) {
- _engine->_gamestate->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
- } else {
- DebugPrintf("Invalid usage of %s\n", argv[0]);
- return true;
- }
+ _engine->_gamestate->abortScriptProcessing = kAbortRestartGame;;
- _engine->_gamestate->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW;
- script_abort_flag = 1;
-
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdClassTable(int argc, const char **argv) {
DebugPrintf("Available classes:\n");
- for (uint i = 0; i < _engine->_gamestate->_segMan->_classtable.size(); i++) {
- if (_engine->_gamestate->_segMan->_classtable[i].reg.segment) {
+ for (uint i = 0; i < _engine->_gamestate->_segMan->classTableSize(); i++) {
+ if (_engine->_gamestate->_segMan->_classTable[i].reg.segment) {
DebugPrintf(" Class 0x%x at %04x:%04x (script 0x%x)\n", i,
- PRINT_REG(_engine->_gamestate->_segMan->_classtable[i].reg),
- _engine->_gamestate->_segMan->_classtable[i].script);
+ PRINT_REG(_engine->_gamestate->_segMan->_classTable[i].reg),
+ _engine->_gamestate->_segMan->_classTable[i].script);
}
}
@@ -1011,8 +1199,9 @@ bool Console::cmdParse(int argc, const char **argv) {
char string[1000];
// Construct the string
- strcpy(string, argv[2]);
+ strcpy(string, argv[1]);
for (int i = 2; i < argc; i++) {
+ strcat(string, " ");
strcat(string, argv[i]);
}
@@ -1044,6 +1233,122 @@ bool Console::cmdParse(int argc, const char **argv) {
return true;
}
+bool Console::cmdSaid(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Matches a string against a said spec\n");
+ DebugPrintf("Usage: %s <string> > & <said spec>\n", argv[0]);
+ DebugPrintf("<string> is a sequence of actual words.\n");
+ DebugPrintf("<said spec> is a sequence of hex tokens.\n");
+ return true;
+ }
+
+ ResultWordList words;
+ char *error;
+ char string[1000];
+ byte spec[1000];
+
+ int p;
+ // Construct the string
+ strcpy(string, argv[1]);
+ for (p = 2; p < argc && strcmp(argv[p],"&") != 0; p++) {
+ strcat(string, " ");
+ strcat(string, argv[p]);
+ }
+
+ if (p >= argc-1) {
+ DebugPrintf("Matches a string against a said spec\n");
+ DebugPrintf("Usage: %s <string> > & <said spec>\n", argv[0]);
+ DebugPrintf("<string> is a sequence of actual words.\n");
+ DebugPrintf("<said spec> is a sequence of hex tokens.\n");
+ return true;
+ }
+
+ // TODO: Maybe turn this into a proper said spec compiler
+ unsigned int len = 0;
+ for (p++; p < argc; p++) {
+ if (strcmp(argv[p], ",") == 0) {
+ spec[len++] = 0xf0;
+ } else if (strcmp(argv[p], "&") == 0) {
+ spec[len++] = 0xf1;
+ } else if (strcmp(argv[p], "/") == 0) {
+ spec[len++] = 0xf2;
+ } else if (strcmp(argv[p], "(") == 0) {
+ spec[len++] = 0xf3;
+ } else if (strcmp(argv[p], ")") == 0) {
+ spec[len++] = 0xf4;
+ } else if (strcmp(argv[p], "[") == 0) {
+ spec[len++] = 0xf5;
+ } else if (strcmp(argv[p], "]") == 0) {
+ spec[len++] = 0xf6;
+ } else if (strcmp(argv[p], "#") == 0) {
+ spec[len++] = 0xf7;
+ } else if (strcmp(argv[p], "<") == 0) {
+ spec[len++] = 0xf8;
+ } else if (strcmp(argv[p], ">") == 0) {
+ spec[len++] = 0xf9;
+ } else if (strcmp(argv[p], "[<") == 0) {
+ spec[len++] = 0xf5;
+ spec[len++] = 0xf8;
+ } else if (strcmp(argv[p], "[/") == 0) {
+ spec[len++] = 0xf5;
+ spec[len++] = 0xf2;
+ } else if (strcmp(argv[p], "!*") == 0) {
+ spec[len++] = 0x0f;
+ spec[len++] = 0xfe;
+ } else if (strcmp(argv[p], "[!*]") == 0) {
+ spec[len++] = 0xf5;
+ spec[len++] = 0x0f;
+ spec[len++] = 0xfe;
+ spec[len++] = 0xf6;
+ } else {
+ unsigned int s = strtol(argv[p], 0, 16);
+ if (s >= 0xf0 && s <= 0xff) {
+ spec[len++] = s;
+ } else {
+ spec[len++] = s >> 8;
+ spec[len++] = s & 0xFF;
+ }
+ }
+ }
+ spec[len++] = 0xFF;
+
+ printf("Matching '%s' against:", string);
+ _engine->getVocabulary()->debugDecipherSaidBlock(spec);
+ printf("\n");
+
+ bool res = _engine->getVocabulary()->tokenizeString(words, string, &error);
+ if (res && !words.empty()) {
+ int syntax_fail = 0;
+
+ _engine->getVocabulary()->synonymizeTokens(words);
+
+ DebugPrintf("Parsed to the following blocks:\n");
+
+ for (ResultWordList::const_iterator i = words.begin(); i != words.end(); ++i)
+ DebugPrintf(" Type[%04x] Group[%04x]\n", i->_class, i->_group);
+
+ if (_engine->getVocabulary()->parseGNF(words, true))
+ syntax_fail = 1; // Building a tree failed
+
+ if (syntax_fail)
+ DebugPrintf("Building a tree failed.\n");
+ else {
+ _engine->getVocabulary()->dumpParseTree();
+ _engine->getVocabulary()->parserIsValid = true;
+
+ int ret = said(_engine->_gamestate, (byte*)spec, true);
+ DebugPrintf("kSaid: %s\n", (ret == SAID_NO_MATCH ? "No match" : "Match"));
+ }
+
+ } else {
+ DebugPrintf("Unknown word: '%s'\n", error);
+ free(error);
+ }
+
+ return true;
+}
+
+
bool Console::cmdParserNodes(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Shows the specified number of nodes from the parse node tree\n");
@@ -1115,7 +1420,11 @@ bool Console::cmdDrawRobot(int argc, const char **argv) {
uint16 resourceId = atoi(argv[1]);
- _engine->_gui32->drawRobot(resourceId);
+ if (_engine->_gfxPaint32) {
+ _engine->_gfxPaint32->debugDrawRobot(resourceId);
+ } else {
+ DebugPrintf("command not available in non-sci32 games");
+ }
return true;
}
#endif
@@ -1129,7 +1438,11 @@ bool Console::cmdUndither(int argc, const char **argv) {
bool flag = atoi(argv[1]) ? true : false;
_engine->_gfxScreen->debugUnditherSetState(flag);
- return false;
+ if (flag)
+ DebugPrintf("undithering ENABLED\n");
+ else
+ DebugPrintf("undithering DISABLED\n");
+ return true;
}
bool Console::cmdPicVisualize(int argc, const char **argv) {
@@ -1141,7 +1454,16 @@ bool Console::cmdPicVisualize(int argc, const char **argv) {
bool state = atoi(argv[1]) ? true : false;
- return _engine->_gui->debugEGAdrawingVisualize(state);
+ if (_engine->_resMan->getViewType() == kViewEga) {
+ _engine->_gfxPaint16->debugSetEGAdrawingVisualize(state);
+ if (state)
+ DebugPrintf("picture visualization ENABLED\n");
+ else
+ DebugPrintf("picture visualization DISABLED\n");
+ } else {
+ DebugPrintf("picture visualization only available for EGA games\n");
+ }
+ return true;
}
bool Console::cmdPlayVideo(int argc, const char **argv) {
@@ -1159,7 +1481,7 @@ bool Console::cmdPlayVideo(int argc, const char **argv) {
if (filename.hasSuffix(".seq") || filename.hasSuffix(".avi") || filename.hasSuffix(".vmd")) {
_videoFile = filename;
_videoFrameDelay = (argc == 2) ? 10 : atoi(argv[2]);
- return false;
+ return Cmd_Exit(0, 0);
} else {
DebugPrintf("Unknown video file type\n");
return true;
@@ -1184,7 +1506,7 @@ bool Console::cmdPrintSegmentTable(int argc, const char **argv) {
switch (mobj->getType()) {
case SEG_TYPE_SCRIPT:
- DebugPrintf("S script.%03d l:%d ", (*(Script *)mobj)._nr, (*(Script *)mobj).getLockers());
+ DebugPrintf("S script.%03d l:%d ", (*(Script *)mobj).getScriptNumber(), (*(Script *)mobj).getLockers());
break;
case SEG_TYPE_CLONES:
@@ -1219,9 +1541,15 @@ bool Console::cmdPrintSegmentTable(int argc, const char **argv) {
DebugPrintf("M dynmem: %d bytes", (*(DynMem *)mobj)._size);
break;
- case SEG_TYPE_STRING_FRAG:
- DebugPrintf("F string fragments");
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_ARRAY:
+ DebugPrintf("A SCI32 arrays (%d)", (*(ArrayTable *)mobj).entries_used);
+ break;
+
+ case SEG_TYPE_STRING:
+ DebugPrintf("T SCI32 strings (%d)", (*(StringTable *)mobj).entries_used);
break;
+#endif
default:
DebugPrintf("I Invalid (type = %x)", mobj->getType());
@@ -1248,13 +1576,13 @@ bool Console::segmentInfo(int nr) {
case SEG_TYPE_SCRIPT: {
Script *scr = (Script *)mobj;
- DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->_nr, scr->getLockers(), (uint)scr->_bufSize, (uint)scr->_bufSize);
- if (scr->_exportTable)
- DebugPrintf(" Exports: %4d at %d\n", scr->_numExports, (int)(((byte *)scr->_exportTable) - ((byte *)scr->_buf)));
+ DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->getScriptNumber(), scr->getLockers(), (uint)scr->getBufSize(), (uint)scr->getBufSize());
+ if (scr->getExportTable())
+ DebugPrintf(" Exports: %4d at %d\n", scr->getExportsNr(), (int)(((const byte *)scr->getExportTable()) - ((const byte *)scr->getBuf())));
else
DebugPrintf(" Exports: none\n");
- DebugPrintf(" Synonyms: %4d\n", scr->_numSynonyms);
+ DebugPrintf(" Synonyms: %4d\n", scr->getSynonymsNr());
if (scr->_localsBlock)
DebugPrintf(" Locals : %4d in segment 0x%x\n", scr->_localsBlock->_locals.size(), scr->_localsSegment);
@@ -1268,7 +1596,7 @@ bool Console::segmentInfo(int nr) {
for (it = scr->_objects.begin(); it != end; ++it) {
DebugPrintf(" ");
// Object header
- Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos());
+ const Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos());
if (obj)
DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(it->_value.getPos()),
_engine->_gamestate->_segMan->getObjectName(it->_value.getPos()),
@@ -1315,7 +1643,7 @@ bool Console::segmentInfo(int nr) {
objpos.segment = nr;
DebugPrintf(" [%04x] %s; copy of ", i, _engine->_gamestate->_segMan->getObjectName(objpos));
// Object header
- Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos());
+ const Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos());
if (obj)
DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct->_table[i].getPos()),
_engine->_gamestate->_segMan->getObjectName(ct->_table[i].getPos()),
@@ -1361,10 +1689,14 @@ bool Console::segmentInfo(int nr) {
}
break;
- case SEG_TYPE_STRING_FRAG: {
- DebugPrintf("string frags\n");
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_STRING:
+ DebugPrintf("SCI32 strings\n");
break;
- }
+ case SEG_TYPE_ARRAY:
+ DebugPrintf("SCI32 arrays\n");
+ break;
+#endif
default :
DebugPrintf("Invalid type %d\n", mobj->getType());
@@ -1380,7 +1712,7 @@ bool Console::cmdSegmentInfo(int argc, const char **argv) {
DebugPrintf("Provides information on the specified segment(s)\n");
DebugPrintf("Usage: %s <segment number>\n", argv[0]);
DebugPrintf("<segment number> can be a number, which shows the information of the segment with\n");
- DebugPrintf("the specified number, or \"all\" to show information on all active segments");
+ DebugPrintf("the specified number, or \"all\" to show information on all active segments\n");
return true;
}
@@ -1388,9 +1720,11 @@ bool Console::cmdSegmentInfo(int argc, const char **argv) {
for (uint i = 0; i < _engine->_gamestate->_segMan->_heap.size(); i++)
segmentInfo(i);
} else {
- int nr = atoi(argv[1]);
- if (!segmentInfo(nr))
- DebugPrintf("Segment %04x does not exist\n", nr);
+ int segmentNr;
+ if (!parseInteger(argv[1], segmentNr))
+ return true;
+ if (!segmentInfo(segmentNr))
+ DebugPrintf("Segment %04xh does not exist\n", segmentNr);
}
return true;
@@ -1403,21 +1737,23 @@ bool Console::cmdKillSegment(int argc, const char **argv) {
DebugPrintf("Usage: %s <segment number>\n", argv[0]);
return true;
}
-
- _engine->_gamestate->_segMan->getScript(atoi(argv[1]))->setLockers(0);
+ int segmentNumber;
+ if (!parseInteger(argv[1], segmentNumber))
+ return true;
+ _engine->_gamestate->_segMan->getScript(segmentNumber)->setLockers(0);
return true;
}
bool Console::cmdShowMap(int argc, const char **argv) {
if (argc != 2) {
- DebugPrintf("Shows one of the screen maps\n");
+ DebugPrintf("Switches to one of the following screen maps\n");
DebugPrintf("Usage: %s <screen map>\n", argv[0]);
DebugPrintf("Screen maps:\n");
- DebugPrintf("- 0: visual map (back buffer)\n");
- DebugPrintf("- 1: priority map (back buffer)\n");
- DebugPrintf("- 2: control map (static buffer)\n");
- DebugPrintf("- 3: display screen (newgui only)\n");
+ DebugPrintf("- 0: visual map\n");
+ DebugPrintf("- 1: priority map\n");
+ DebugPrintf("- 2: control map\n");
+ DebugPrintf("- 3: display screen\n");
return true;
}
@@ -1435,28 +1771,12 @@ bool Console::cmdShowMap(int argc, const char **argv) {
DebugPrintf("Map %d is not available.\n", map);
return true;
}
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdSongLib(int argc, const char **argv) {
DebugPrintf("Song library:\n");
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- Song *seeker = _engine->_gamestate->_sound._songlib._lib;
-
- do {
- DebugPrintf(" %p", (void *)seeker);
-
- if (seeker) {
- DebugPrintf("[%04lx,p=%d,s=%d]->", seeker->_handle, seeker->_priority, seeker->_status);
- seeker = seeker->_next;
- }
- DebugPrintf("\n");
- } while (seeker);
- DebugPrintf("\n");
-#else
- _engine->_gamestate->_soundCmd->printPlayList(this);
-#endif
+ g_sci->_soundCmd->printPlayList(this);
return true;
}
@@ -1476,7 +1796,7 @@ bool Console::cmdSongInfo(int argc, const char **argv) {
return true;
}
- _engine->_gamestate->_soundCmd->printSongInfo(addr, this);
+ g_sci->_soundCmd->printSongInfo(addr, this);
return true;
}
@@ -1495,9 +1815,8 @@ bool Console::cmdStartSound(int argc, const char **argv) {
return true;
}
- _engine->_gamestate->_soundCmd->startNewSound(number);
-
- return false;
+ g_sci->_soundCmd->startNewSound(number);
+ return Cmd_Exit(0, 0);
}
bool Console::cmdToggleSound(int argc, const char **argv) {
@@ -1519,37 +1838,21 @@ bool Console::cmdToggleSound(int argc, const char **argv) {
return true;
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- int handle = id.segment << 16 | id.offset; // frobnicate handle
-
- if (id.segment) {
- SegManager *segMan = _engine->_gamestate->_segMan; // for PUT_SEL32V
- _engine->_gamestate->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- _engine->_gamestate->_sound.sfx_remove_song(handle);
- PUT_SEL32V(segMan, id, SELECTOR(signal), SIGNAL_OFFSET);
- PUT_SEL32V(segMan, id, SELECTOR(nodePtr), 0);
- PUT_SEL32V(segMan, id, SELECTOR(handle), 0);
- }
-#else
-
Common::String newState = argv[2];
newState.toLowercase();
if (newState == "play")
- _engine->_gamestate->_soundCmd->playSound(id);
+ g_sci->_soundCmd->processPlaySound(id);
else if (newState == "stop")
- _engine->_gamestate->_soundCmd->stopSound(id);
+ g_sci->_soundCmd->processStopSound(id, false);
else
DebugPrintf("New state can either be 'play' or 'stop'");
-#endif
return true;
}
bool Console::cmdStopAllSounds(int argc, const char **argv) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- _engine->_gamestate->_soundCmd->stopAllSounds();
-#endif
+ g_sci->_soundCmd->stopAllSounds();
DebugPrintf("All sounds have been stopped\n");
return true;
@@ -1563,36 +1866,6 @@ bool Console::cmdIsSample(int argc, const char **argv) {
return true;
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- Resource *song = _engine->getResMan()->findResource(ResourceId(kResourceTypeSound, atoi(argv[1])), 0);
- SongIterator *songit;
- Audio::AudioStream *data;
-
- if (!song) {
- DebugPrintf("Not a sound resource!\n");
- return true;
- }
-
- songit = songit_new(song->data, song->size, SCI_SONG_ITERATOR_TYPE_SCI0, 0xcaffe /* What do I care about the ID? */);
-
- if (!songit) {
- DebugPrintf("Could not convert to song iterator!\n");
- return true;
- }
-
- data = songit->getAudioStream();
- if (data) {
- // TODO
-/*
- DebugPrintf("\nIs sample (encoding %dHz/%s/%04x)", data->conf.rate, (data->conf.stereo) ?
- ((data->conf.stereo == SFX_PCM_STEREO_LR) ? "stereo-LR" : "stereo-RL") : "mono", data->conf.format);
-*/
- delete data;
- } else
- DebugPrintf("Valid song, but not a sample.\n");
-
- delete songit;
-#else
int16 number = atoi(argv[1]);
if (!_engine->getResMan()->testResource(ResourceId(kResourceTypeSound, number))) {
@@ -1616,7 +1889,6 @@ bool Console::cmdIsSample(int argc, const char **argv) {
DebugPrintf("Sample size: %d, sample rate: %d, channels: %d, digital channel number: %d\n",
track->digitalSampleSize, track->digitalSampleRate, track->channelCount, track->digitalChannelNr);
-#endif
return true;
}
@@ -1628,10 +1900,10 @@ bool Console::cmdGCInvoke(int argc, const char **argv) {
}
bool Console::cmdGCObjects(int argc, const char **argv) {
- reg_t_hash_map *use_map = find_all_used_references(_engine->_gamestate);
+ AddrSet *use_map = findAllActiveReferences(_engine->_gamestate);
DebugPrintf("Reachable object references (normalised):\n");
- for (reg_t_hash_map::iterator i = use_map->begin(); i != use_map->end(); ++i) {
+ for (AddrSet::iterator i = use_map->begin(); i != use_map->end(); ++i) {
DebugPrintf(" - %04x:%04x\n", PRINT_REG(i->_key));
}
@@ -1640,11 +1912,6 @@ bool Console::cmdGCObjects(int argc, const char **argv) {
return true;
}
-void _print_address(void * _, reg_t addr) {
- if (addr.segment)
- g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(addr));
-}
-
bool Console::cmdGCShowReachable(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Prints all addresses directly reachable from the memory object specified as parameter.\n");
@@ -1668,7 +1935,10 @@ bool Console::cmdGCShowReachable(int argc, const char **argv) {
}
DebugPrintf("Reachable from %04x:%04x:\n", PRINT_REG(addr));
- mobj->listAllOutgoingReferences(addr, NULL, _print_address);
+ const Common::Array<reg_t> tmp = mobj->listAllOutgoingReferences(addr);
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+ if (it->segment)
+ g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(*it));
return true;
}
@@ -1697,7 +1967,10 @@ bool Console::cmdGCShowFreeable(int argc, const char **argv) {
}
DebugPrintf("Freeable in segment %04x:\n", addr.segment);
- mobj->listAllDeallocatable(addr.segment, NULL, _print_address);
+ const Common::Array<reg_t> tmp = mobj->listAllDeallocatable(addr.segment);
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+ if (it->segment)
+ g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(*it));
return true;
}
@@ -1733,14 +2006,15 @@ bool Console::cmdGCNormalize(int argc, const char **argv) {
}
bool Console::cmdVMVarlist(int argc, const char **argv) {
+ EngineState *s = _engine->_gamestate;
const char *varnames[] = {"global", "local", "temp", "param"};
DebugPrintf("Addresses of variables in the VM:\n");
for (int i = 0; i < 4; i++) {
- DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(scriptState.variables_seg[i], scriptState.variables[i] - scriptState.variables_base[i])));
- if (scriptState.variables_max)
- DebugPrintf(" total %d", scriptState.variables_max[i]);
+ DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(s->variablesSegment[i], s->variables[i] - s->variablesBase[i])));
+ if (s->variablesMax)
+ DebugPrintf(" total %d", s->variablesMax[i]);
DebugPrintf("\n");
}
@@ -1748,55 +2022,95 @@ bool Console::cmdVMVarlist(int argc, const char **argv) {
}
bool Console::cmdVMVars(int argc, const char **argv) {
- if (argc < 3) {
+ if (argc < 2) {
DebugPrintf("Displays or changes variables in the VM\n");
DebugPrintf("Usage: %s <type> <varnum> [<value>]\n", argv[0]);
- DebugPrintf("First parameter is either g(lobal), l(ocal), t(emp) or p(aram).\n");
- DebugPrintf("Second parameter is the var number\n");
+ DebugPrintf("First parameter is either g(lobal), l(ocal), t(emp), p(aram) or a(cc).\n");
+ DebugPrintf("Second parameter is the var number (not specified on acc)\n");
DebugPrintf("Third parameter (if specified) is the value to set the variable to, in address form\n");
DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
return true;
}
- const char *varnames[] = {"global", "local", "temp", "param"};
- const char *varabbrev = "gltp";
- const char *vartype_pre = strchr(varabbrev, *argv[1]);
- int vartype;
- int idx = atoi(argv[2]);
-
- if (!vartype_pre) {
+ EngineState *s = _engine->_gamestate;
+ const char *varNames[] = {"global", "local", "temp", "param", "acc"};
+ const char *varAbbrev = "gltpa";
+ const char *varType_pre = strchr(varAbbrev, *argv[1]);
+ int varType;
+ int varIndex = 0;
+ reg_t *curValue = NULL;
+ const char *setValue = NULL;
+
+ if (!varType_pre) {
DebugPrintf("Invalid variable type '%c'\n", *argv[1]);
return true;
}
- vartype = vartype_pre - varabbrev;
+ varType = varType_pre - varAbbrev;
- if (idx < 0) {
- DebugPrintf("Invalid: negative index\n");
- return true;
- }
+ switch (varType) {
+ case 0:
+ case 1:
+ case 2:
+ case 3: {
+ // for global, local, temp and param, we need an index
+ if (argc < 3) {
+ DebugPrintf("Variable number must be specified for requested type\n");
+ return true;
+ }
+ if (argc > 4) {
+ DebugPrintf("Too many arguments\n");
+ return true;
+ }
- if ((scriptState.variables_max) && (scriptState.variables_max[vartype] <= idx)) {
- DebugPrintf("Max. index is %d (0x%x)\n", scriptState.variables_max[vartype], scriptState.variables_max[vartype]);
- return true;
- }
+ if (!parseInteger(argv[2], varIndex))
+ return true;
- switch (argc) {
- case 3:
- DebugPrintf("%s var %d == %04x:%04x\n", varnames[vartype], idx, PRINT_REG(scriptState.variables[vartype][idx]));
+ if (varIndex < 0) {
+ DebugPrintf("Variable number may not be negative\n");
+ return true;
+ }
+
+ if ((s->variablesMax) && (s->variablesMax[varType] <= varIndex)) {
+ DebugPrintf("Maximum variable number for this type is %d (0x%x)\n", s->variablesMax[varType], s->variablesMax[varType]);
+ return true;
+ }
+ curValue = &s->variables[varType][varIndex];
+ if (argc == 4)
+ setValue = argv[3];
break;
+ }
+
case 4:
- if (parse_reg_t(_engine->_gamestate, argv[3], &scriptState.variables[vartype][idx], true)) {
- DebugPrintf("Invalid value/address passed.\n");
- DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
- DebugPrintf("Or pass a decimal or hexadecimal value directly (e.g. 12, 1Ah)\n");
+ // acc
+ if (argc > 3) {
+ DebugPrintf("Too many arguments\n");
return true;
}
+ curValue = &s->r_acc;
+ if (argc == 3)
+ setValue = argv[2];
break;
+
default:
- DebugPrintf("Too many arguments\n");
+ break;
}
+ if (!setValue) {
+ if (varType == 4)
+ DebugPrintf("%s == %04x:%04x", varNames[varType], PRINT_REG(*curValue));
+ else
+ DebugPrintf("%s var %d == %04x:%04x", varNames[varType], varIndex, PRINT_REG(*curValue));
+ printBasicVarInfo(*curValue);
+ DebugPrintf("\n");
+ } else {
+ if (parse_reg_t(s, setValue, curValue, true)) {
+ DebugPrintf("Invalid value/address passed.\n");
+ DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
+ DebugPrintf("Or pass a decimal or hexadecimal value directly (e.g. 12, 1Ah)\n");
+ return true;
+ }
+ }
return true;
}
@@ -1846,20 +2160,22 @@ bool Console::cmdValueType(int argc, const char **argv) {
int t = g_sci->getKernel()->findRegType(val);
switch (t) {
- case KSIG_LIST:
+ case SIG_TYPE_LIST:
DebugPrintf("List");
break;
- case KSIG_OBJECT:
+ case SIG_TYPE_OBJECT:
DebugPrintf("Object");
break;
- case KSIG_REF:
+ case SIG_TYPE_REFERENCE:
DebugPrintf("Reference");
break;
- case KSIG_ARITHMETIC:
- DebugPrintf("Arithmetic");
+ case SIG_TYPE_INTEGER:
+ DebugPrintf("Integer");
+ case SIG_TYPE_INTEGER | SIG_TYPE_NULL:
+ DebugPrintf("Null");
break;
default:
- DebugPrintf("Erroneous unknown type %02x(%d decimal)\n", t, t);
+ DebugPrintf("Erroneous unknown type 0x%02x (%d decimal)\n", t, t);
}
return true;
@@ -1923,7 +2239,7 @@ bool Console::cmdViewReference(int argc, const char **argv) {
return true;
}
- if (reg_end.segment != reg.segment) {
+ if (reg_end.segment != reg.segment && reg_end != NULL_REG) {
DebugPrintf("Ending segment different from starting segment. Assuming no bound on dump.\n");
reg_end = NULL_REG;
}
@@ -1939,47 +2255,68 @@ bool Console::cmdViewReference(int argc, const char **argv) {
switch (type) {
case 0:
break;
- case KSIG_LIST: {
- List *l = _engine->_gamestate->_segMan->lookupList(reg);
+ case SIG_TYPE_LIST: {
+ List *list = _engine->_gamestate->_segMan->lookupList(reg);
DebugPrintf("list\n");
- if (l)
- printList(l);
+ if (list)
+ printList(list);
else
DebugPrintf("Invalid list.\n");
}
break;
- case KSIG_NODE:
+ case SIG_TYPE_NODE:
DebugPrintf("list node\n");
printNode(reg);
break;
- case KSIG_OBJECT:
+ case SIG_TYPE_OBJECT:
DebugPrintf("object\n");
printObject(reg);
break;
- case KSIG_REF: {
- int size;
- const SegmentRef block = _engine->_gamestate->_segMan->dereference(reg);
- size = block.maxSize;
+ case SIG_TYPE_REFERENCE: {
+ switch (_engine->_gamestate->_segMan->getSegmentType(reg.segment)) {
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_STRING: {
+ DebugPrintf("SCI32 string\n");
+ const SciString *str = _engine->_gamestate->_segMan->lookupString(reg);
+ Common::hexdump((const byte *) str->getRawData(), str->getSize(), 16, 0);
+ break;
+ }
+ case SEG_TYPE_ARRAY: {
+ DebugPrintf("SCI32 array:\n");
+ const SciArray<reg_t> *array = _engine->_gamestate->_segMan->lookupArray(reg);
+ hexDumpReg(array->getRawData(), array->getSize(), 4, 0, true);
+ break;
+ }
+#endif
+ default: {
+ int size;
+ const SegmentRef block = _engine->_gamestate->_segMan->dereference(reg);
+ size = block.maxSize;
- DebugPrintf("raw data\n");
+ DebugPrintf("raw data\n");
- if (reg_end.segment != 0 && size < reg_end.offset - reg.offset) {
- DebugPrintf("Block end out of bounds (size %d). Resetting.\n", size);
- reg_end = NULL_REG;
- }
+ if (reg_end.segment != 0 && size < reg_end.offset - reg.offset) {
+ DebugPrintf("Block end out of bounds (size %d). Resetting.\n", size);
+ reg_end = NULL_REG;
+ }
- if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset))
- size = reg_end.offset - reg.offset;
+ if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset))
+ size = reg_end.offset - reg.offset;
- if (reg_end.segment != 0)
- DebugPrintf("Block size less than or equal to %d\n", size);
+ if (reg_end.segment != 0)
+ DebugPrintf("Block size less than or equal to %d\n", size);
- Common::hexdump(block.raw, size, 16, 0);
+ if (block.isRaw)
+ Common::hexdump(block.raw, size, 16, 0);
+ else
+ hexDumpReg(block.reg, size / 2, 4, 0);
+ }
}
break;
- case KSIG_ARITHMETIC:
+ }
+ case SIG_TYPE_INTEGER:
DebugPrintf("arithmetic value\n %d (%04x)\n", (int16) reg.offset, reg.offset);
break;
default:
@@ -2019,7 +2356,7 @@ bool Console::cmdViewObject(int argc, const char **argv) {
bool Console::cmdViewActiveObject(int argc, const char **argv) {
DebugPrintf("Information on the currently active object or class:\n");
- printObject(scriptState.xs->objp);
+ printObject(_engine->_gamestate->xs->objp);
return true;
}
@@ -2032,35 +2369,12 @@ bool Console::cmdViewAccumulatorObject(int argc, const char **argv) {
}
bool Console::cmdScriptSteps(int argc, const char **argv) {
- DebugPrintf("Number of executed SCI operations: %d\n", script_step_counter);
- return true;
-}
-
-bool Console::cmdSetAccumulator(int argc, const char **argv) {
- if (argc != 2) {
- DebugPrintf("Sets the accumulator.\n");
- DebugPrintf("Usage: %s <address>\n", argv[0]);
- DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
- return true;
- }
-
- reg_t val;
-
- if (parse_reg_t(_engine->_gamestate, argv[1], &val, false)) {
- DebugPrintf("Invalid address passed.\n");
- DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
- return true;
- }
-
- _engine->_gamestate->r_acc = val;
-
+ DebugPrintf("Number of executed SCI operations: %d\n", _engine->_gamestate->scriptStepCounter);
return true;
}
bool Console::cmdBacktrace(int argc, const char **argv) {
- DebugPrintf("Dumping the send/self/super/call/calle/callb stack:\n");
-
- DebugPrintf("Call stack (current base: 0x%x):\n", _engine->_gamestate->execution_stack_base);
+ DebugPrintf("Call stack (current base: 0x%x):\n", _engine->_gamestate->executionStackBase);
Common::List<ExecStack>::iterator iter;
uint i = 0;
@@ -2071,20 +2385,25 @@ bool Console::cmdBacktrace(int argc, const char **argv) {
int paramc, totalparamc;
switch (call.type) {
-
- case EXEC_STACK_TYPE_CALL: {// Normal function
- DebugPrintf(" %x:[%x] %s::%s(", i, call.origin, objname, (call.selector == -1) ? "<call[be]?>" :
- selector_name(_engine->_gamestate, call.selector));
- }
- break;
+ case EXEC_STACK_TYPE_CALL: // Normal function
+ if (call.type == EXEC_STACK_TYPE_CALL)
+ DebugPrintf(" %x: script %d - ", i, (*(Script *)_engine->_gamestate->_segMan->_heap[call.addr.pc.segment]).getScriptNumber());
+ if (call.debugSelector != -1) {
+ DebugPrintf("%s::%s(", objname, _engine->getKernel()->getSelectorName(call.debugSelector).c_str());
+ } else if (call.debugExportId != -1) {
+ DebugPrintf("export %d (", call.debugExportId);
+ } else if (call.debugLocalCallOffset != -1) {
+ DebugPrintf("call %x (", call.debugLocalCallOffset);
+ }
+ break;
case EXEC_STACK_TYPE_KERNEL: // Kernel function
- DebugPrintf(" %x:[%x] k%s(", i, call.origin, _engine->getKernel()->getKernelName(call.selector).c_str());
+ DebugPrintf(" %x:[%x] k%s(", i, call.debugOrigin, _engine->getKernel()->getKernelName(call.debugSelector).c_str());
break;
case EXEC_STACK_TYPE_VARSELECTOR:
- DebugPrintf(" %x:[%x] vs%s %s::%s (", i, call.origin, (call.argc) ? "write" : "read",
- objname, _engine->getKernel()->getSelectorName(call.selector).c_str());
+ DebugPrintf(" %x:[%x] vs%s %s::%s (", i, call.debugOrigin, (call.argc) ? "write" : "read",
+ objname, _engine->getKernel()->getSelectorName(call.debugSelector).c_str());
break;
}
@@ -2103,7 +2422,10 @@ bool Console::cmdBacktrace(int argc, const char **argv) {
if (call.argc > 16)
DebugPrintf("...");
- DebugPrintf(")\n obj@%04x:%04x", PRINT_REG(call.objp));
+ DebugPrintf(")\n ");
+ if (call.debugOrigin != -1)
+ DebugPrintf("by %x ", call.debugOrigin);
+ DebugPrintf("obj@%04x:%04x", PRINT_REG(call.objp));
if (call.type == EXEC_STACK_TYPE_CALL) {
DebugPrintf(" pc=%04x:%04x", PRINT_REG(call.addr.pc));
if (call.sp == CALL_SP_CARRY)
@@ -2116,35 +2438,41 @@ bool Console::cmdBacktrace(int argc, const char **argv) {
DebugPrintf(" pc:none");
DebugPrintf(" argp:ST:%04x", (unsigned)(call.variables_argp - _engine->_gamestate->stack_base));
- if (call.type == EXEC_STACK_TYPE_CALL)
- DebugPrintf(" script: %d", (*(Script *)_engine->_gamestate->_segMan->_heap[call.addr.pc.segment])._nr);
DebugPrintf("\n");
}
return true;
}
-bool Console::cmdStep(int argc, const char **argv) {
+bool Console::cmdTrace(int argc, const char **argv) {
if (argc == 2 && atoi(argv[1]) > 0)
- g_debugState.runningStep = atoi(argv[1]) - 1;
- g_debugState.debugging = true;
+ _debugState.runningStep = atoi(argv[1]) - 1;
+ _debugState.debugging = true;
+
+ return Cmd_Exit(0, 0);
+}
+
+bool Console::cmdStepOver(int argc, const char **argv) {
+ _debugState.seeking = kDebugSeekStepOver;
+ _debugState.seekLevel = _engine->_gamestate->_executionStack.size();
+ _debugState.debugging = true;
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdStepEvent(int argc, const char **argv) {
- g_debugState.stopOnEvent = true;
- g_debugState.debugging = true;
+ _debugState.stopOnEvent = true;
+ _debugState.debugging = true;
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdStepRet(int argc, const char **argv) {
- g_debugState.seeking = kDebugSeekLevelRet;
- g_debugState.seekLevel = _engine->_gamestate->_executionStack.size() - 1;
- g_debugState.debugging = true;
+ _debugState.seeking = kDebugSeekLevelRet;
+ _debugState.seekLevel = _engine->_gamestate->_executionStack.size() - 1;
+ _debugState.debugging = true;
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdStepGlobal(int argc, const char **argv) {
@@ -2154,11 +2482,11 @@ bool Console::cmdStepGlobal(int argc, const char **argv) {
return true;
}
- g_debugState.seeking = kDebugSeekGlobal;
- g_debugState.seekSpecial = atoi(argv[1]);
- g_debugState.debugging = true;
+ _debugState.seeking = kDebugSeekGlobal;
+ _debugState.seekSpecial = atoi(argv[1]);
+ _debugState.debugging = true;
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdStepCallk(int argc, const char **argv) {
@@ -2184,14 +2512,14 @@ bool Console::cmdStepCallk(int argc, const char **argv) {
}
}
- g_debugState.seeking = kDebugSeekSpecialCallk;
- g_debugState.seekSpecial = callk_index;
+ _debugState.seeking = kDebugSeekSpecialCallk;
+ _debugState.seekSpecial = callk_index;
} else {
- g_debugState.seeking = kDebugSeekCallk;
+ _debugState.seeking = kDebugSeekCallk;
}
- g_debugState.debugging = true;
+ _debugState.debugging = true;
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdDisassemble(int argc, const char **argv) {
@@ -2209,8 +2537,8 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
return true;
}
- Object *obj = _engine->_gamestate->_segMan->getObject(objAddr);
- int selector_id = _engine->getKernel()->findSelector(argv[2]);
+ const Object *obj = _engine->_gamestate->_segMan->getObject(objAddr);
+ int selectorId = _engine->getKernel()->findSelector(argv[2]);
reg_t addr;
if (!obj) {
@@ -2218,12 +2546,12 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
return true;
}
- if (selector_id < 0) {
+ if (selectorId < 0) {
DebugPrintf("Not a valid selector name.");
return true;
}
- if (lookup_selector(_engine->_gamestate->_segMan, objAddr, selector_id, NULL, &addr) != kSelectorMethod) {
+ if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) {
DebugPrintf("Not a method.");
return true;
}
@@ -2302,24 +2630,24 @@ bool Console::cmdSend(int argc, const char **argv) {
return true;
}
- const char *selector_name = argv[2];
- int selector_id = _engine->getKernel()->findSelector(selector_name);
+ const char *selectorName = argv[2];
+ int selectorId = _engine->getKernel()->findSelector(selectorName);
- if (selector_id < 0) {
- DebugPrintf("Unknown selector: \"%s\"\n", selector_name);
+ if (selectorId < 0) {
+ DebugPrintf("Unknown selector: \"%s\"\n", selectorName);
return true;
}
- Object *o = _engine->_gamestate->_segMan->getObject(object);
+ const Object *o = _engine->_gamestate->_segMan->getObject(object);
if (o == NULL) {
DebugPrintf("Address \"%04x:%04x\" is not an object\n", PRINT_REG(object));
return true;
}
- SelectorType selector_type = lookup_selector(_engine->_gamestate->_segMan, object, selector_id, 0, 0);
+ SelectorType selector_type = lookupSelector(_engine->_gamestate->_segMan, object, selectorId, NULL, NULL);
if (selector_type == kSelectorNone) {
- DebugPrintf("Object does not support selector: \"%s\"\n", selector_name);
+ DebugPrintf("Object does not support selector: \"%s\"\n", selectorName);
return true;
}
@@ -2329,7 +2657,7 @@ bool Console::cmdSend(int argc, const char **argv) {
// Create the data block for send_selecor() at the top of the stack:
// [selector_number][argument_counter][arguments...]
StackPtr stackframe = _engine->_gamestate->_executionStack.back().sp;
- stackframe[0] = make_reg(0, selector_id);
+ stackframe[0] = make_reg(0, selectorId);
stackframe[1] = make_reg(0, send_argc);
for (int i = 0; i < send_argc; i++) {
if (parse_reg_t(_engine->_gamestate, argv[3+i], &stackframe[2+i], false)) {
@@ -2356,7 +2684,7 @@ bool Console::cmdSend(int argc, const char **argv) {
// We call run_engine explictly so we can restore the value of r_acc
// after execution.
- run_vm(_engine->_gamestate, 0);
+ run_vm(_engine->_gamestate);
}
@@ -2371,19 +2699,44 @@ bool Console::cmdSend(int argc, const char **argv) {
bool Console::cmdGo(int argc, const char **argv) {
// CHECKME: is this necessary?
- g_debugState.seeking = kDebugSeekNothing;
+ _debugState.seeking = kDebugSeekNothing;
return Cmd_Exit(argc, argv);
}
+bool Console::cmdLogKernel(int argc, const char **argv) {
+ if (argc < 3) {
+ DebugPrintf("Logs calls to specified kernel function.\n");
+ DebugPrintf("Usage: %s <kernel-function/*> <on/off>\n", argv[0]);
+ DebugPrintf("Example: %s StrCpy on\n", argv[0]);
+ return true;
+ }
+
+ bool logging;
+ if (strcmp(argv[2], "on") == 0)
+ logging = true;
+ else if (strcmp(argv[2], "off") == 0)
+ logging = false;
+ else {
+ DebugPrintf("2nd parameter must be either on or off\n");
+ return true;
+ }
+
+ if (g_sci->getKernel()->debugSetFunction(argv[1], logging, -1))
+ DebugPrintf("Logging %s for k%s\n", logging ? "enabled" : "disabled", argv[1]);
+ else
+ DebugPrintf("Unknown kernel function %s\n", argv[1]);
+ return true;
+}
+
bool Console::cmdBreakpointList(int argc, const char **argv) {
int i = 0;
int bpdata;
DebugPrintf("Breakpoint list:\n");
- Common::List<Breakpoint>::const_iterator bp = g_debugState._breakpoints.begin();
- Common::List<Breakpoint>::const_iterator end = g_debugState._breakpoints.end();
+ Common::List<Breakpoint>::const_iterator bp = _debugState._breakpoints.begin();
+ Common::List<Breakpoint>::const_iterator end = _debugState._breakpoints.end();
for (; bp != end; ++bp) {
DebugPrintf(" #%i: ", i);
switch (bp->type) {
@@ -2399,6 +2752,9 @@ bool Console::cmdBreakpointList(int argc, const char **argv) {
i++;
}
+ if (!i)
+ DebugPrintf(" No breakpoints defined.\n");
+
return true;
}
@@ -2406,14 +2762,21 @@ bool Console::cmdBreakpointDelete(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Deletes a breakpoint with the specified index.\n");
DebugPrintf("Usage: %s <breakpoint index>\n", argv[0]);
+ DebugPrintf("<index> * will remove all breakpoints\n");
+ return true;
+ }
+
+ if (strcmp(argv[1], "*") == 0) {
+ _debugState._breakpoints.clear();
+ _debugState._activeBreakpointTypes = 0;
return true;
}
const int idx = atoi(argv[1]);
// Find the breakpoint at index idx.
- Common::List<Breakpoint>::iterator bp = g_debugState._breakpoints.begin();
- const Common::List<Breakpoint>::iterator end = g_debugState._breakpoints.end();
+ Common::List<Breakpoint>::iterator bp = _debugState._breakpoints.begin();
+ const Common::List<Breakpoint>::iterator end = _debugState._breakpoints.end();
for (int i = 0; bp != end && i < idx; ++bp, ++i) {
// do nothing
}
@@ -2424,23 +2787,23 @@ bool Console::cmdBreakpointDelete(int argc, const char **argv) {
}
// Delete it
- g_debugState._breakpoints.erase(bp);
+ _debugState._breakpoints.erase(bp);
// Update EngineState::_activeBreakpointTypes.
int type = 0;
- for (bp = g_debugState._breakpoints.begin(); bp != end; ++bp) {
+ for (bp = _debugState._breakpoints.begin(); bp != end; ++bp) {
type |= bp->type;
}
- g_debugState._activeBreakpointTypes = type;
+ _debugState._activeBreakpointTypes = type;
return true;
}
-bool Console::cmdBreakpointExecMethod(int argc, const char **argv) {
+bool Console::cmdBreakpointMethod(int argc, const char **argv) {
if (argc != 2) {
- DebugPrintf("Sets a breakpoint on the execution of the specified method.\n");
- DebugPrintf("Usage: %s <method name>\n", argv[0]);
+ DebugPrintf("Sets a breakpoint on execution/access of a specified method/selector.\n");
+ DebugPrintf("Usage: %s <name>\n", argv[0]);
DebugPrintf("Example: %s ego::doit\n", argv[0]);
DebugPrintf("May also be used to set a breakpoint that applies whenever an object\n");
DebugPrintf("of a specific type is touched: %s foo::\n", argv[0]);
@@ -2454,13 +2817,29 @@ bool Console::cmdBreakpointExecMethod(int argc, const char **argv) {
bp.type = BREAK_SELECTOR;
bp.name = argv[1];
- g_debugState._breakpoints.push_back(bp);
- g_debugState._activeBreakpointTypes |= BREAK_SELECTOR;
+ _debugState._breakpoints.push_back(bp);
+ _debugState._activeBreakpointTypes |= BREAK_SELECTOR;
return true;
}
-bool Console::cmdBreakpointExecFunction(int argc, const char **argv) {
+bool Console::cmdBreakpointKernel(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Sets a breakpoint on execution of a kernel function.\n");
+ DebugPrintf("Usage: %s <name>\n", argv[0]);
+ DebugPrintf("Example: %s DrawPic\n", argv[0]);
+ return true;
+ }
+
+ if (g_sci->getKernel()->debugSetFunction(argv[1], -1, true))
+ DebugPrintf("Breakpoint enabled for k%s\n", argv[1]);
+ else
+ DebugPrintf("Unknown kernel function %s\n", argv[1]);
+
+ return true;
+}
+
+bool Console::cmdBreakpointFunction(int argc, const char **argv) {
// TODO/FIXME: Why does this accept 2 parameters (the high and the low part of the address)?"
if (argc != 3) {
DebugPrintf("Sets a breakpoint on the execution of the specified exported function.\n");
@@ -2475,8 +2854,8 @@ bool Console::cmdBreakpointExecFunction(int argc, const char **argv) {
bp.type = BREAK_EXPORT;
bp.address = (atoi(argv[1]) << 16 | atoi(argv[2]));
- g_debugState._breakpoints.push_back(bp);
- g_debugState._activeBreakpointTypes |= BREAK_EXPORT;
+ _debugState._breakpoints.push_back(bp);
+ _debugState._activeBreakpointTypes |= BREAK_EXPORT;
return true;
}
@@ -2676,16 +3055,16 @@ bool Console::cmdQuit(int argc, const char **argv) {
if (!scumm_stricmp(argv[1], "game")) {
// Quit gracefully
- script_abort_flag = 1; // Terminate VM
- g_debugState.seeking = kDebugSeekNothing;
- g_debugState.runningStep = 0;
+ _engine->_gamestate->abortScriptProcessing = kAbortQuitGame; // Terminate VM
+ _debugState.seeking = kDebugSeekNothing;
+ _debugState.runningStep = 0;
} else if (!scumm_stricmp(argv[1], "now")) {
// Quit ungracefully
exit(0);
}
- return false;
+ return Cmd_Exit(0, 0);
}
bool Console::cmdAddresses(int argc, const char **argv) {
@@ -2925,8 +3304,71 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV
return 0;
}
-void Console::printList(List *l) {
- reg_t pos = l->first;
+bool Console::parseInteger(const char *argument, int &result) {
+ char *endPtr = 0;
+ int idxLen = strlen(argument);
+ const char *lastChar = argument + idxLen - (idxLen == 0 ? 0 : 1);
+
+ if ((strncmp(argument, "0x", 2) == 0) || (*lastChar == 'h')) {
+ // hexadecimal number
+ result = strtol(argument, &endPtr, 16);
+ if ((*endPtr != 0) && (*endPtr != 'h')) {
+ DebugPrintf("Invalid hexadecimal number '%s'\n", argument);
+ return false;
+ }
+ } else {
+ // decimal number
+ result = strtol(argument, &endPtr, 10);
+ if (*endPtr != 0) {
+ DebugPrintf("Invalid decimal number '%s'\n", argument);
+ return false;
+ }
+ }
+ return true;
+}
+
+void Console::printBasicVarInfo(reg_t variable) {
+ int regType = g_sci->getKernel()->findRegType(variable);
+ int segType = regType;
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+ segType &= SIG_TYPE_INTEGER | SIG_TYPE_OBJECT | SIG_TYPE_REFERENCE | SIG_TYPE_NODE | SIG_TYPE_LIST | SIG_TYPE_UNINITIALIZED | SIG_TYPE_ERROR;
+
+ switch (segType) {
+ case SIG_TYPE_INTEGER: {
+ uint16 content = variable.toUint16();
+ if (content >= 10)
+ DebugPrintf(" (%dd)", content);
+ break;
+ }
+ case SIG_TYPE_OBJECT:
+ DebugPrintf(" (object '%s')", segMan->getObjectName(variable));
+ break;
+ case SIG_TYPE_REFERENCE:
+ DebugPrintf(" (reference)");
+ break;
+ case SIG_TYPE_NODE:
+ DebugPrintf(" (node)");
+ break;
+ case SIG_TYPE_LIST:
+ DebugPrintf(" (list)");
+ break;
+ case SIG_TYPE_UNINITIALIZED:
+ DebugPrintf(" (uninitialized)");
+ break;
+ case SIG_TYPE_ERROR:
+ DebugPrintf(" (error)");
+ break;
+ default:
+ DebugPrintf(" (??\?)");
+ }
+
+ if (regType & SIG_IS_INVALID)
+ DebugPrintf(" IS INVALID!");
+}
+
+void Console::printList(List *list) {
+ reg_t pos = list->first;
reg_t my_prev = NULL_REG;
DebugPrintf("\t<\n");
@@ -2953,9 +3395,9 @@ void Console::printList(List *l) {
pos = node->succ;
}
- if (my_prev != l->last)
+ if (my_prev != list->last)
DebugPrintf(" WARNING: Last node was expected to be %04x:%04x, was %04x:%04x!\n",
- PRINT_REG(l->last), PRINT_REG(my_prev));
+ PRINT_REG(list->last), PRINT_REG(my_prev));
DebugPrintf("\t>\n");
}
@@ -3001,8 +3443,8 @@ int Console::printNode(reg_t addr) {
int Console::printObject(reg_t pos) {
EngineState *s = _engine->_gamestate; // for the several defines in this function
- Object *obj = s->_segMan->getObject(pos);
- Object *var_container = obj;
+ const Object *obj = s->_segMan->getObject(pos);
+ const Object *var_container = obj;
uint i;
if (!obj) {
@@ -3014,14 +3456,14 @@ int Console::printObject(reg_t pos) {
DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), s->_segMan->getObjectName(pos),
obj->getVarCount(), obj->getMethodCount());
- if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS))
+ if (!obj->isClass())
var_container = s->_segMan->getObject(obj->getSuperClassSelector());
DebugPrintf(" -- member variables:\n");
for (i = 0; (uint)i < obj->getVarCount(); i++) {
DebugPrintf(" ");
if (i < var_container->getVarCount()) {
uint16 varSelector = var_container->getVarSelector(i);
- DebugPrintf("[%03x] %s = ", varSelector, selector_name(s, varSelector));
+ DebugPrintf("[%03x] %s = ", varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str());
} else
DebugPrintf("p#%x = ", i);
@@ -3031,7 +3473,7 @@ int Console::printObject(reg_t pos) {
if (!val.segment)
DebugPrintf(" (%d)", val.offset);
- Object *ref = s->_segMan->getObject(val);
+ const Object *ref = s->_segMan->getObject(val);
if (ref)
DebugPrintf(" (%s)", s->_segMan->getObjectName(val));
@@ -3040,12 +3482,66 @@ int Console::printObject(reg_t pos) {
DebugPrintf(" -- methods:\n");
for (i = 0; i < obj->getMethodCount(); i++) {
reg_t fptr = obj->getFunction(i);
- DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), selector_name(s, obj->getFuncSelector(i)), PRINT_REG(fptr));
+ DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), _engine->getKernel()->getSelectorName(obj->getFuncSelector(i)).c_str(), PRINT_REG(fptr));
}
if (s->_segMan->_heap[pos.segment]->getType() == SEG_TYPE_SCRIPT)
- DebugPrintf("\nOwner script:\t%d\n", s->_segMan->getScript(pos.segment)->_nr);
+ DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.segment)->getScriptNumber());
return 0;
}
+void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startOffset, bool isArray) {
+ // reg_t version of Common::hexdump
+ assert(1 <= regsPerLine && regsPerLine <= 8);
+ int i;
+ byte c;
+ int offset = startOffset;
+ while (len >= regsPerLine) {
+ printf("%06x: ", offset);
+ for (i = 0; i < regsPerLine; i++) {
+ printf("%04x:%04x ", PRINT_REG(data[i]));
+ }
+ printf(" |");
+ for (i = 0; i < regsPerLine; i++) {
+ c = data[i].toUint16() & 0xff;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ c = data[i].toUint16() >> 8;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ }
+ printf("|\n");
+ data += regsPerLine;
+ len -= regsPerLine;
+ offset += regsPerLine * (isArray ? 1 : 2);
+ }
+
+ if (len <= 0)
+ return;
+
+ printf("%06x: ", offset);
+ for (i = 0; i < regsPerLine; i++) {
+ if (i < len)
+ printf("%04x:%04x ", PRINT_REG(data[i]));
+ else
+ printf(" ");
+ }
+ printf(" |");
+ for (i = 0; i < len; i++) {
+ c = data[i].toUint16() & 0xff;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ c = data[i].toUint16() >> 8;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ }
+ for (; i < regsPerLine; i++)
+ printf(" ");
+ printf("|\n");
+}
+
} // End of namespace Sci
diff --git a/engines/sci/console.h b/engines/sci/console.h
index c88795ae26..60599ea783 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -65,6 +65,7 @@ private:
bool cmdSentenceFragments(int argc, const char **argv);
bool cmdParse(int argc, const char **argv);
bool cmdSetParseNodes(int argc, const char **argv);
+ bool cmdSaid(int argc, const char **argv);
// Resources
bool cmdDiskDump(int argc, const char **argv);
bool cmdHexDump(int argc, const char **argv);
@@ -73,6 +74,8 @@ private:
bool cmdResourceTypes(int argc, const char **argv);
bool cmdList(int argc, const char **argv);
bool cmdHexgrep(int argc, const char **argv);
+ bool cmdVerifyScripts(int argc, const char **argv);
+ bool cmdShowInstruments(int argc, const char **argv);
// Game
bool cmdSaveGame(int argc, const char **argv);
bool cmdRestoreGame(int argc, const char **argv);
@@ -116,9 +119,9 @@ private:
bool cmdAddresses(int argc, const char **argv);
bool cmdRegisters(int argc, const char **argv);
bool cmdDissectScript(int argc, const char **argv);
- bool cmdSetAccumulator(int argc, const char **argv);
bool cmdBacktrace(int argc, const char **argv);
- bool cmdStep(int argc, const char **argv);
+ bool cmdTrace(int argc, const char **argv);
+ bool cmdStepOver(int argc, const char **argv);
bool cmdStepEvent(int argc, const char **argv);
bool cmdStepRet(int argc, const char **argv);
bool cmdStepGlobal(int argc, const char **argv);
@@ -127,11 +130,13 @@ private:
bool cmdDisassembleAddress(int argc, const char **argv);
bool cmdSend(int argc, const char **argv);
bool cmdGo(int argc, const char **argv);
+ bool cmdLogKernel(int argc, const char **argv);
// Breakpoints
bool cmdBreakpointList(int argc, const char **argv);
bool cmdBreakpointDelete(int argc, const char **argv);
- bool cmdBreakpointExecMethod(int argc, const char **argv);
- bool cmdBreakpointExecFunction(int argc, const char **argv);
+ bool cmdBreakpointMethod(int argc, const char **argv);
+ bool cmdBreakpointKernel(int argc, const char **argv);
+ bool cmdBreakpointFunction(int argc, const char **argv);
// VM
bool cmdScriptSteps(int argc, const char **argv);
bool cmdVMVarlist(int argc, const char **argv);
@@ -144,15 +149,22 @@ private:
bool cmdViewActiveObject(int argc, const char **argv);
bool cmdViewAccumulatorObject(int argc, const char **argv);
+ bool parseInteger(const char *argument, int &result);
+
+ void printBasicVarInfo(reg_t variable);
+
bool segmentInfo(int nr);
- void printList(List *l);
+ void printList(List *list);
int printNode(reg_t addr);
+ void hexDumpReg(const reg_t *data, int len, int regsPerLine = 4, int startOffset = 0, bool isArray = false);
private:
SciEngine *_engine;
+ DebugState &_debugState;
bool _mouseVisible;
Common::String _videoFile;
int _videoFrameDelay;
+ uint32 _enterTime;
};
} // End of namespace Sci
diff --git a/engines/sci/debug.h b/engines/sci/debug.h
index 8383722956..5cf0e38fbc 100644
--- a/engines/sci/debug.h
+++ b/engines/sci/debug.h
@@ -26,8 +26,8 @@
#ifndef SCI_DEBUG_H
#define SCI_DEBUG_H
+#include "common/list.h"
#include "sci/engine/vm_types.h" // for StackPtr
-#include "sci/engine/vm.h" // for ExecStack
namespace Sci {
@@ -57,8 +57,8 @@ enum DebugSeeking {
kDebugSeekCallk = 1, // Step forward until callk is found
kDebugSeekLevelRet = 2, // Step forward until returned from this level
kDebugSeekSpecialCallk = 3, // Step forward until a /special/ callk is found
- kDebugSeekSO = 4, // Step forward until specified PC (after the send command) and stack depth
- kDebugSeekGlobal = 5 // Step forward until one specified global variable is modified
+ kDebugSeekGlobal = 4, // Step forward until one specified global variable is modified
+ kDebugSeekStepOver = 5 // Step forward until we reach same stack-level again
};
struct DebugState {
@@ -79,7 +79,6 @@ struct DebugState {
extern int g_debug_sleeptime_factor;
extern int g_debug_simulated_key;
extern bool g_debug_track_mouse_clicks;
-extern DebugState g_debugState;
} // End of namespace Sci
diff --git a/engines/sci/decompressor.cpp b/engines/sci/decompressor.cpp
index 84af50b596..96c7f24ef6 100644
--- a/engines/sci/decompressor.cpp
+++ b/engines/sci/decompressor.cpp
@@ -574,15 +574,17 @@ void DecompressorLZW::reorderView(byte *src, byte *dest) {
for (c = 0; c < cel_total; c++)
decodeRLE(&rle_ptr, &pix_ptr, cc_pos[c] + 8, cc_lengths[c]);
- *writer++ = 'P';
- *writer++ = 'A';
- *writer++ = 'L';
+ if (pal_offset) {
+ *writer++ = 'P';
+ *writer++ = 'A';
+ *writer++ = 'L';
- for (c = 0; c < 256; c++)
- *writer++ = c;
+ for (c = 0; c < 256; c++)
+ *writer++ = c;
- seeker -= 4; /* The missing four. Don't ask why. */
- memcpy(writer, seeker, 4*256 + 4);
+ seeker -= 4; /* The missing four. Don't ask why. */
+ memcpy(writer, seeker, 4*256 + 4);
+ }
free(cc_pos);
free(cc_lengths);
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 2a83c8e32a..e330bd5f30 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -36,12 +36,11 @@
#include "sci/engine/script.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
-#include "sci/engine/vm.h" // for convertSierraGameId
namespace Sci {
// Titles of the games
-static const PlainGameDescriptor SciGameTitles[] = {
+static const PlainGameDescriptor s_sciGameTitles[] = {
{"sci", "Sierra SCI Game"},
{"sci-fanmade", "Fanmade SCI Game"},
// === SCI0 games =========================================================
@@ -57,7 +56,7 @@ static const PlainGameDescriptor SciGameTitles[] = {
{"lsl2", "Leisure Suit Larry 2: Goes Looking for Love (in Several Wrong Places)"},
{"lsl3", "Leisure Suit Larry 3: Passionate Patti in Pursuit of the Pulsating Pectorals"},
{"pq2", "Police Quest II: The Vengeance"},
- {"qfg1", "Quest for Glory I: So You Want to Be a Hero"}, // EGA is SCI0, VGA SCI1.1
+ {"qfg1", "Quest for Glory I: So You Want to Be a Hero"},
{"sq3", "Space Quest III: The Pirates of Pestulon"},
// === SCI01 games ========================================================
{"qfg2", "Quest for Glory II: Trial by Fire"},
@@ -90,11 +89,12 @@ static const PlainGameDescriptor SciGameTitles[] = {
{"hoyle4", "Hoyle Classic Card Games"},
{"kq6", "King's Quest VI: Heir Today, Gone Tomorrow"},
{"laurabow2", "Laura Bow 2: The Dagger of Amon Ra"},
+ {"qfg1vga", "Quest for Glory I: So You Want to Be a Hero"},
{"qfg3", "Quest for Glory III: Wages of War"},
{"sq5", "Space Quest V: The Next Mutation"},
{"islandbrain", "The Island of Dr. Brain"},
{"lsl6", "Leisure Suit Larry 6: Shape Up or Slip Out!"},
- {"mothergoose", "Mixed-Up Mother Goose"}, // floppy is SCI1.1, CD SCI2.1
+ {"mothergoose", "Mixed-Up Mother Goose"},
{"pepper", "Pepper's Adventure in Time"},
{"slater", "Slater & Charlie Go Camping"},
// === SCI2 games =========================================================
@@ -106,6 +106,8 @@ static const PlainGameDescriptor SciGameTitles[] = {
// TODO: Inside The Chest/Behind the Developer's Shield
{"kq7", "King's Quest VII: The Princeless Bride"},
// TODO: King's Questions
+ {"lsl6hires", "Leisure Suit Larry 6: Shape Up or Slip Out!"},
+ {"mothergoosehires","Mixed-Up Mother Goose"},
{"phantasmagoria", "Phantasmagoria"},
{"pqswat", "Police Quest: SWAT"},
{"shivers", "Shivers"},
@@ -120,6 +122,237 @@ static const PlainGameDescriptor SciGameTitles[] = {
{0, 0}
};
+struct GameIdStrToEnum {
+ const char *gameidStr;
+ SciGameId gameidEnum;
+};
+
+static const GameIdStrToEnum s_gameIdStrToEnum[] = {
+ { "astrochicken", GID_ASTROCHICKEN },
+ { "camelot", GID_CAMELOT },
+ { "castlebrain", GID_CASTLEBRAIN },
+ { "christmas1988", GID_CHRISTMAS1988 },
+ { "christmas1990", GID_CHRISTMAS1990 },
+ { "christmas1992", GID_CHRISTMAS1992 },
+ { "cnick-kq", GID_CNICK_KQ },
+ { "cnick-laurabow", GID_CNICK_LAURABOW },
+ { "cnick-longbow", GID_CNICK_LONGBOW },
+ { "cnick-lsl", GID_CNICK_LSL },
+ { "cnick-sq", GID_CNICK_SQ },
+ { "ecoquest", GID_ECOQUEST },
+ { "ecoquest2", GID_ECOQUEST2 },
+ { "fairytales", GID_FAIRYTALES },
+ { "freddypharkas", GID_FREDDYPHARKAS },
+ { "funseeker", GID_FUNSEEKER },
+ { "gk1", GID_GK1 },
+ { "gk2", GID_GK2 },
+ { "hoyle1", GID_HOYLE1 },
+ { "hoyle2", GID_HOYLE2 },
+ { "hoyle3", GID_HOYLE3 },
+ { "hoyle4", GID_HOYLE4 },
+ { "iceman", GID_ICEMAN },
+ { "islandbrain", GID_ISLANDBRAIN },
+ { "jones", GID_JONES },
+ { "kq1sci", GID_KQ1 },
+ { "kq4sci", GID_KQ4 },
+ { "kq5", GID_KQ5 },
+ { "kq6", GID_KQ6 },
+ { "kq7", GID_KQ7 },
+ { "laurabow", GID_LAURABOW },
+ { "laurabow2", GID_LAURABOW2 },
+ { "lighthouse", GID_LIGHTHOUSE },
+ { "longbow", GID_LONGBOW },
+ { "lsl1sci", GID_LSL1 },
+ { "lsl2", GID_LSL2 },
+ { "lsl3", GID_LSL3 },
+ { "lsl5", GID_LSL5 },
+ { "lsl6", GID_LSL6 },
+ { "lsl6hires", GID_LSL6HIRES },
+ { "lsl7", GID_LSL7 },
+ { "mothergoose", GID_MOTHERGOOSE },
+ { "mothergoosehires",GID_MOTHERGOOSEHIRES },
+ { "msastrochicken", GID_MSASTROCHICKEN },
+ { "pepper", GID_PEPPER },
+ { "phantasmagoria", GID_PHANTASMAGORIA },
+ { "phantasmagoria2", GID_PHANTASMAGORIA2 },
+ { "pq1sci", GID_PQ1 },
+ { "pq2", GID_PQ2 },
+ { "pq3", GID_PQ3 },
+ { "pq4", GID_PQ4 },
+ { "pqswat", GID_PQSWAT },
+ { "qfg1", GID_QFG1 },
+ { "qfg1vga", GID_QFG1VGA },
+ { "qfg2", GID_QFG2 },
+ { "qfg3", GID_QFG3 },
+ { "qfg4", GID_QFG4 },
+ { "rama", GID_RAMA },
+ { "sci-fanmade", GID_FANMADE }, // FIXME: Do we really need/want this?
+ { "shivers", GID_SHIVERS },
+ { "shivers2", GID_SHIVERS2 },
+ { "slater", GID_SLATER },
+ { "sq1sci", GID_SQ1 },
+ { "sq3", GID_SQ3 },
+ { "sq4", GID_SQ4 },
+ { "sq5", GID_SQ5 },
+ { "sq6", GID_SQ6 },
+ { "torin", GID_TORIN },
+ { NULL, (SciGameId)-1 }
+};
+
+struct OldNewIdTableEntry {
+ const char *oldId;
+ const char *newId;
+ SciVersion version;
+};
+
+static const OldNewIdTableEntry s_oldNewTable[] = {
+ { "arthur", "camelot", SCI_VERSION_NONE },
+ { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga
+ { "brain", "castlebrain", SCI_VERSION_1_LATE },
+ { "demo", "christmas1988", SCI_VERSION_NONE },
+ { "card", "christmas1990", SCI_VERSION_1_EARLY, },
+ { "card", "christmas1992", SCI_VERSION_1_1 },
+ { "RH Budget", "cnick-longbow", SCI_VERSION_NONE },
+ // iceman is the same
+ { "icedemo", "iceman", SCI_VERSION_NONE },
+ // longbow is the same
+ { "eco", "ecoquest", SCI_VERSION_NONE },
+ { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo
+ { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full
+ { "tales", "fairytales", SCI_VERSION_NONE },
+ { "fp", "freddypharkas", SCI_VERSION_NONE },
+ { "emc", "funseeker", SCI_VERSION_NONE },
+ { "gk", "gk1", SCI_VERSION_NONE },
+ // gk2 is the same
+ { "hoyledemo", "hoyle1", SCI_VERSION_NONE },
+ { "cardgames", "hoyle1", SCI_VERSION_NONE },
+ { "solitare", "hoyle2", SCI_VERSION_NONE },
+ // hoyle3 is the same
+ // hoyle4 is the same
+ { "brain", "islandbrain", SCI_VERSION_1_1 },
+ { "demo000", "kq1sci", SCI_VERSION_NONE },
+ { "kq1", "kq1sci", SCI_VERSION_NONE },
+ { "kq4", "kq4sci", SCI_VERSION_NONE },
+ // kq5 is the same
+ // kq6 is the same
+ // kq7 is the same
+ { "mm1", "laurabow", SCI_VERSION_NONE },
+ { "cb1", "laurabow", SCI_VERSION_NONE },
+ { "lb2", "laurabow2", SCI_VERSION_NONE },
+ { "rh", "longbow", SCI_VERSION_NONE },
+ { "ll1", "lsl1sci", SCI_VERSION_NONE },
+ { "lsl1", "lsl1sci", SCI_VERSION_NONE },
+ // lsl2 is the same
+ { "lsl3", "lsl3", SCI_VERSION_NONE },
+ { "ll5", "lsl5", SCI_VERSION_NONE },
+ // lsl5 is the same
+ // lsl6 is the same
+ { "mg", "mothergoose", SCI_VERSION_NONE },
+ { "twisty", "pepper", SCI_VERSION_NONE },
+ { "scary", "phantasmagoria", SCI_VERSION_NONE },
+ // TODO: distinguish the full version of Phantasmagoria from the demo
+ { "pq1", "pq1sci", SCI_VERSION_NONE },
+ { "pq", "pq2", SCI_VERSION_NONE },
+ // pq3 is the same
+ // pq4 is the same
+ { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA
+ { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA
+ { "trial", "qfg2", SCI_VERSION_NONE },
+ { "hq2demo", "qfg2", SCI_VERSION_NONE },
+ // rama is the same
+ // TODO: distinguish the full version of rama from the demo
+ { "thegame", "slater", SCI_VERSION_NONE },
+ { "sq1demo", "sq1sci", SCI_VERSION_NONE },
+ { "sq1", "sq1sci", SCI_VERSION_NONE },
+ // sq3 is the same
+ // sq4 is the same
+ // sq5 is the same
+ // sq6 is the same
+ // TODO: distinguish the full version of SQ6 from the demo
+ // torin is the same
+
+
+ // TODO: SCI3 IDs
+
+ { "", "", SCI_VERSION_NONE }
+};
+
+/**
+ * Converts the builtin Sierra game IDs to the ones we use in ScummVM
+ * @param[in] gameId The internal game ID
+ * @param[in] gameFlags The game's flags, which are adjusted accordingly for demos
+ * @return The equivalent ScummVM game id
+ */
+Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, ResourceManager *resMan) {
+ // Convert the id to lower case, so that we match all upper/lower case variants.
+ sierraId.toLowercase();
+
+ // If the game has less than the expected scripts, it's a demo
+ uint32 demoThreshold = 100;
+ // ...but there are some exceptions
+ if (sierraId == "brain" || sierraId == "lsl1" ||
+ sierraId == "mg" || sierraId == "pq" ||
+ sierraId == "jones" ||
+ sierraId == "cardgames" || sierraId == "solitare" ||
+ sierraId == "hoyle3" || sierraId == "hoyle4")
+ demoThreshold = 40;
+ if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4")
+ demoThreshold = 150;
+
+ Common::ScopedPtr<Common::List<ResourceId> > resources(resMan->listResources(kResourceTypeScript, -1));
+ if (resources->size() < demoThreshold) {
+ *gameFlags |= ADGF_DEMO;
+
+ // Crazy Nick's Picks
+ if (sierraId == "lsl1" && resources->size() == 34)
+ return "cnick-lsl";
+ if (sierraId == "sq4" && resources->size() == 34)
+ return "cnick-sq";
+
+ // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read)
+
+ // Handle Astrochicken 1 (SQ3) and 2 (SQ4)
+ if (sierraId == "sq3" && resources->size() == 20)
+ return "astrochicken";
+ if (sierraId == "sq4")
+ return "msastrochicken";
+ }
+
+ if (sierraId == "torin" && resources->size() == 226) // Torin's Passage demo
+ *gameFlags |= ADGF_DEMO;
+
+ for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) {
+ if (sierraId == cur->oldId) {
+ // Distinguish same IDs from the SCI version
+ if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion())
+ continue;
+
+ return cur->newId;
+ }
+ }
+
+ if (sierraId == "glory") {
+ // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1),
+ // or qfg4 full (SCI2)
+ // qfg1 VGA doesn't have view 1
+ if (!resMan->testResource(ResourceId(kResourceTypeView, 1)))
+ return "qfg1vga";
+
+ // qfg4 full is SCI2
+ if (getSciVersion() == SCI_VERSION_2)
+ return "qfg4";
+
+ // qfg4 demo has less than 50 scripts
+ if (resources->size() < 50)
+ return "qfg4";
+
+ // Otherwise it's qfg3
+ return "qfg3";
+ }
+
+ return sierraId;
+}
+
#include "sci/detection_tables.h"
/**
@@ -147,7 +380,7 @@ static const ADParams detectionParams = {
// Number of bytes to compute MD5 sum for
5000,
// List of all engine targets
- SciGameTitles,
+ s_sciGameTitles,
// Structure for autoupgrading obsolete targets
0,
// Name of single gameid (optional)
@@ -157,7 +390,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class SciMetaEngine : public AdvancedMetaEngine {
@@ -205,50 +442,6 @@ Common::Language charToScummVMLanguage(const char c) {
}
}
-#define READ_UINT16(buf) (!resMan->isSci11Mac() ? READ_LE_UINT16(buf) : READ_BE_UINT16(buf))
-
-// Finds the internal ID of the current game from script 0
-Common::String getSierraGameId(ResourceManager *resMan) {
- Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, 0), false);
- Script *script000 = new Script();
- script000->init(0, resMan);
- script000->mcpyInOut(0, script->data, script->size);
- uint16 curOffset = (getSciVersion() == SCI_VERSION_0_EARLY) ? 2 : 0;
- uint16 objLength = 0;
- int objType = 0;
- int16 exportsOffset = 0;
- Common::String sierraId;
-
- // Now find the export table of the script
- do {
- objType = READ_UINT16(script000->_buf + curOffset);
- if (!objType)
- break;
-
- objLength = READ_UINT16(script000->_buf + curOffset + 2);
- curOffset += 4; // skip header
-
- if (objType == SCI_OBJ_EXPORTS) {
- exportsOffset = READ_UINT16(script000->_buf + curOffset + 2);
- break;
- }
- curOffset += objLength - 4;
- } while (objType != 0 && curOffset < script->size - 2);
-
- // The game object is the first export. Script 0 is always at segment 1
- reg_t gameObj = make_reg(1, exportsOffset);
-
- // TODO: stop using the segment manager and read the object name here
- SegManager *segMan = new SegManager(resMan);
- script_instantiate(resMan, segMan, 0);
- sierraId = segMan->getObjectName(gameObj);
- delete segMan;
-
- delete script000;
-
- return sierraId;
-}
-
const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fslist) const {
bool foundResMap = false;
bool foundRes000 = false;
@@ -269,19 +462,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
filename.toLowercase();
if (filename.contains("resource.map") || filename.contains("resmap.00") || filename.contains("Data1")) {
- // HACK: resource.map is located in the same directory as the other resource files,
- // therefore add the directory here, so that the game files can be opened later on
- // We now add the parent directory temporary to our SearchMan so the engine code
- // used in the detection can access all files via Common::File without any problems.
- // In all branches returning from this function, we need to have a call to
- // SearchMan.remove to remove it from the default directory pool again.
- //
- // A proper solution to remove this hack would be to have the code, which is needed
- // for detection, to operate on Stream objects, so they can be easily called from
- // the detection code. This might be easily to achieve through refactoring the
- // code needed for detection.
- assert(!SearchMan.hasArchive("SCI_detection"));
- SearchMan.addDirectory("SCI_detection", file->getParent());
foundResMap = true;
}
@@ -316,8 +496,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
filename.contains("patch.005") || filename.contains("bank.001"))
s_fallbackDesc.platform = Common::kPlatformAmiga;
- // The existence of 7.pat indicates a Mac game
- if (filename.contains("7.pat"))
+ // The existence of 7.pat or patch.200 indicates a Mac game
+ if (filename.contains("7.pat") || filename.contains("patch.200"))
s_fallbackDesc.platform = Common::kPlatformMacintosh;
// The data files for Atari ST versions are the same as their DOS counterparts
@@ -325,19 +505,22 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
// If these files aren't found, it can't be SCI
if (!foundResMap && !foundRes000) {
- SearchMan.remove("SCI_detection");
return 0;
}
- ResourceManager *resMan = new ResourceManager(fslist);
+ ResourceManager *resMan = new ResourceManager();
+ assert(resMan);
+ resMan->addAppropriateSources(fslist);
+ resMan->init();
+ // TODO: Add error handling.
+
ViewType gameViews = resMan->getViewType();
// Have we identified the game views? If not, stop here
+ // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files
+ // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI
if (gameViews == kViewUnknown) {
- SearchMan.remove("SCI_detection");
delete resMan;
- // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files
- // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI
return 0;
}
@@ -345,7 +528,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
// Is SCI32 compiled in? If not, and this is a SCI32 game,
// stop here
if (getSciVersion() >= SCI_VERSION_2) {
- SearchMan.remove("SCI_detection");
delete resMan;
return (const ADGameDescription *)&s_fallbackDesc;
}
@@ -360,7 +542,15 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
s_fallbackDesc.platform = Common::kPlatformAmiga;
// Determine the game id
- Common::String gameId = convertSierraGameId(getSierraGameId(resMan).c_str(), &s_fallbackDesc.flags, resMan);
+ Common::String sierraGameId = resMan->findSierraGameId();
+
+ // If we don't have a game id, the game is not SCI
+ if (sierraGameId.empty()) {
+ delete resMan;
+ return 0;
+ }
+
+ Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan);
strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1);
s_fallbackGameIdBuf[sizeof(s_fallbackGameIdBuf) - 1] = 0; // Make sure string is NULL terminated
s_fallbackDesc.gameid = s_fallbackGameIdBuf;
@@ -394,7 +584,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
}
}
- delete resMan;
// Fill in extras field
if (!strcmp(s_fallbackDesc.gameid, "lsl1sci") ||
@@ -402,24 +591,28 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
!strcmp(s_fallbackDesc.gameid, "sq1sci"))
s_fallbackDesc.extra = "VGA Remake";
- if (!strcmp(s_fallbackDesc.gameid, "qfg1") && !Common::File::exists("resource.001"))
+ if (!strcmp(s_fallbackDesc.gameid, "qfg1vga") && getSciVersion() == SCI_VERSION_1_1)
s_fallbackDesc.extra = "VGA Remake";
// Add "demo" to the description for demos
if (s_fallbackDesc.flags & ADGF_DEMO)
s_fallbackDesc.extra = "demo";
- SearchMan.remove("SCI_detection");
+ delete resMan;
return (const ADGameDescription *)&s_fallbackDesc;
}
-bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
- const ADGameDescription *desc = (const ADGameDescription *)gd;
-
- *engine = new SciEngine(syst, desc);
+bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const GameIdStrToEnum *g = s_gameIdStrToEnum;
+ for (; g->gameidStr; ++g) {
+ if (0 == strcmp(desc->gameid, g->gameidStr)) {
+ *engine = new SciEngine(syst, desc, g->gameidEnum);
+ return true;
+ }
+ }
- return true;
+ return false;
}
bool SciMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -434,9 +627,16 @@ bool SciMetaEngine::hasFeature(MetaEngineFeature f) const {
bool SciEngine::hasFeature(EngineFeature f) const {
return
- //(f == kSupportsRTL) ||
- (f == kSupportsLoadingDuringRuntime);
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime); // ||
//(f == kSupportsSavingDuringRuntime);
+ // We can't allow saving through ScummVM menu, because
+ // a) lots of games don't like saving everywhere (e.g. castle of dr. brain)
+ // b) some games even dont allow saving in certain rooms (e.g. lsl6)
+ // c) somehow some games even get mad when doing this (execstackbase was 1 all of a sudden in lsl3)
+ // d) for sci0/sci01 games we should at least wait till status bar got drawn, although this may not be enough
+ // we can't make sure that the scripts are fine with us saving at a specific location, doing so may work sometimes
+ // and some other times it won't work.
}
SaveStateList SciMetaEngine::listSaves(const char *target) const {
@@ -557,7 +757,7 @@ Common::Error SciEngine::saveGameState(int slot, const char *desc) {
return Common::kWritingFailed;
}
- if (gamestate_save(_gamestate, out, desc, version)) {
+ if (!gamestate_save(_gamestate, out, desc, version)) {
warning("Saving the game state to '%s' failed", fileName.c_str());
return Common::kWritingFailed;
} else {
@@ -573,11 +773,11 @@ Common::Error SciEngine::saveGameState(int slot, const char *desc) {
}
bool SciEngine::canLoadGameStateCurrently() {
- return !_gamestate->execution_stack_base;
+ return !_gamestate->executionStackBase;
}
bool SciEngine::canSaveGameStateCurrently() {
- return !_gamestate->execution_stack_base;
+ return !_gamestate->executionStackBase;
}
} // End of namespace Sci
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index dba4d879aa..0614eff6e6 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -32,7 +32,7 @@ namespace Sci {
{"sci-fanmade", name, { \
{"resource.map", 0, resMapMd5, resMapSize}, \
{"resource.001", 0, resMd5, resSize}, \
- {NULL, 0, NULL, 0}}, lang, Common::kPlatformPC, 0, GUIO_NOSPEECH \
+ AD_LISTEND}, lang, Common::kPlatformPC, 0, GUIO_NOSPEECH \
}
#define FANMADE(name, resMapMd5, resMapSize, resMd5, resSize) FANMADE_L(name, resMapMd5, resMapSize, resMd5, resSize, Common::EN_ANY)
@@ -47,7 +47,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"astrochicken", "", {
{"resource.map", 0, "f3d1be7752d30ba60614533d531e2e98", 474},
{"resource.001", 0, "6fd05926c2199af0af6f72f90d0d7260", 126895},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Castle of Dr. Brain - English Amiga (from www.back2roots.org)
@@ -59,10 +59,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "3fb02ce493f6eacdcc3713851024f80e", 559540},
{"resource.002", 0, "d226d7d3b4f77c4a566913fc310487fc", 792380},
{"resource.003", 0, "d226d7d3b4f77c4a566913fc310487fc", 464348},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
- // Castle of Dr. Brain - German Amiga (from www.back2roots.org)
+ // Castle of Dr. Brain - German Amiga (from www.back2roots.org, also includes english language)
// Executable scanning reports "1.005.001"
// SCI interpreter version 1.000.510
{"castlebrain", "", {
@@ -71,8 +71,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "4e0836fadc324316c1a418125709ba45", 569057},
{"resource.002", 0, "85e51acb5f9c539d66e3c8fe40e17da5", 826309},
{"resource.003", 0, "85e51acb5f9c539d66e3c8fe40e17da5", 493638},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Castle of Dr. Brain - English DOS Non-Interactive Demo
// SCI interpreter version 1.000.005
@@ -80,9 +80,22 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "467bb5e3224bb54640c3280032aebff5", 633},
{"resource.000", 0, "9780f040d58182994e22d2e34fab85b0", 67367},
{"resource.001", 0, "2af49dbd8f2e1db4ab09f9310dc91259", 570553},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+ // Castle of Dr. Brain - English DOS Floppy EGA (from omer_mor, bug report #3035349)
+ {"castlebrain", "EGA", {
+ {"resource.map", 0, "88d106f945f7fd9d1aeda961cfec38a9", 2646},
+ {"resource.000", 0, "6e125f4ce3f4f5c35f2617c7b66c6e21", 25325},
+ {"resource.001", 0, "1d806162f6d3cfbe3c0135414efe6f88", 99931},
+ {"resource.002", 0, "6a41a0eb5237778427dddf92ae07cf9b", 294772},
+ {"resource.003", 0, "0c6ab4efb3be4d991ae9762e19f17c92", 306378},
+ {"resource.004", 0, "5e7b90949422de005f80285979972e43", 292423},
+ {"resource.005", 0, "8a5ed3ba96e2eaf18e36fedfaab89419", 297838},
+ {"resource.006", 0, "dceed92e709cad1bd9582809a235b0a0", 266682},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Castle of Dr. Brain - English DOS Floppy (from jvprat)
// Executable scanning reports "1.000.044", Floppy label reports "1.0, 10.30.91", VERSION file reports "1.000"
// SCI interpreter version 1.000.510
@@ -91,7 +104,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 346731},
{"resource.001", 0, "d2f5a1be74ed963fa849a76892be5290", 794832},
{"resource.002", 0, "c0c29c51af66d65cb53f49e785a2d978", 1280907},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Castle of Dr. Brain - English DOS Floppy 1.1
@@ -100,24 +113,39 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 347071},
{"resource.001", 0, "13e81e1839cd7b216d2bb5615c1ca160", 796776},
{"resource.002", 0, "930e416bec196b9703a331d81b3d66f2", 1283812},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Castle of Dr. Brain - Spanish DOS
+ // Castle of Dr. Brain - English DOS Floppy 1.000
+ // Reported by graxer in bug report #3037942
+ {"castlebrain", "", {
+ {"resource.map", 0, "453daa935535cef68d19704c2b1b78a2", 2649},
+ {"resource.000", 0, "6e125f4ce3f4f5c35f2617c7b66c6e21", 25929},
+ {"resource.001", 0, "4891faa2f6594c622e482f0ddce24fb4", 99404},
+ {"resource.002", 0, "aebb56d5d005557ca0d122a03aa85386", 322459},
+ {"resource.003", 0, "278ec1e6132c7be844d433dd23beb318", 335156},
+ {"resource.004", 0, "fca1c3f2be660185206f004bda09f4fb", 333549},
+ {"resource.005", 0, "9294e55da1e83708ad3104b2a3963e18", 327537},
+ {"resource.006", 0, "1d778a0c65cac9ddbab65495e50a94ee", 335281},
+ {"resource.007", 0, "063bb8ce4157c778cf30d1c912c006f1", 335631},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Castle of Dr. Brain - Spanish DOS (also includes english language)
// SCI interpreter version 1.000.510
{"castlebrain", "", {
{"resource.map", 0, "5738c163e014bbe046474de009020b82", 2727},
{"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 1197694},
{"resource.001", 0, "735be4e58957180cfc807d5e18fdffcd", 1433302},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Christmas Card 1988 - English DOS
// SCI interpreter version 0.000.294
{"christmas1988", "", {
{"resource.map", 0, "39485580d34a72997f3d5b3aba4d24f1", 426},
{"resource.001", 0, "11391434f41c834090d7a1e9488ce936", 129739},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Christmas Card 1990: The Seasoned Professional - English DOS (16 Colors)
@@ -125,7 +153,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"christmas1990", "16 Colors", {
{"resource.map", 0, "8f656714a05b94423ac6eb10ee8797d0", 600},
{"resource.001", 0, "acde93e58fca4f7a2a5a220558a94aa8", 272629},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Christmas Card 1990: The Seasoned Professional - English DOS (256 Colors)
@@ -133,7 +161,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"christmas1990", "256 Colors", {
{"resource.map", 0, "44b8f45b841b9b5e17e939a35e443988", 600},
{"resource.001", 0, "acde93e58fca4f7a2a5a220558a94aa8", 335362},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Christmas Card 1992 - English DOS
@@ -141,7 +169,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"christmas1992", "", {
{"resource.map", 0, "f1f8c8a8443f523422af70b4ec85b71c", 318},
{"resource.000", 0, "62fb9256f8e7e6e65a6875efdb7939ac", 203396},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Codename: Iceman - English Amiga (from www.back2roots.org)
@@ -155,7 +183,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "d97a96f1ab91b41cf46a02cc89b0a04e", 619219},
{"resource.004", 0, "8613c45fc771d658e5a505b9a4a54f31", 713382},
{"resource.005", 0, "605b67a9ef199a9bb015745e7c004cf4", 478384},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Codename: Iceman - English DOS Non-Interactive Demo
@@ -163,7 +191,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"iceman", "Demo", {
{"resource.map", 0, "782974f29d8a824782d2d4aea39964e3", 1056},
{"resource.001", 0, "d4b75e280d1c3a97cfef1b0bebff387c", 573647},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Codename: Iceman - English DOS (from jvprat)
@@ -176,7 +204,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "36670a917550757d57df84c96cf9e6d9", 566549},
{"resource.003", 0, "d97a96f1ab91b41cf46a02cc89b0a04e", 624303},
{"resource.004", 0, "8613c45fc771d658e5a505b9a4a54f31", 670883},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Codename: Iceman - English DOS (from FRG)
@@ -188,7 +216,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "250b859381ebf2bf8922bd99683b0cc1", 566464},
{"resource.003", 0, "dc7c5280e7acfaffe6ef2a6c963c5f94", 622118},
{"resource.004", 0, "64f342463f6f35ba71b3509ef696ae3f", 669188},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Codename: Iceman - English DOS 1.023 (from abevi, bug report #2612718)
@@ -202,7 +230,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "dc7c5280e7acfaffe6ef2a6c963c5f94", 330653},
{"resource.006", 0, "08050329aa113a9f14ed99cbfe3536ec", 232942},
{"resource.007", 0, "64f342463f6f35ba71b3509ef696ae3f", 267702},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Conquests of Camelot - English Amiga (from www.back2roots.org)
@@ -217,7 +245,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "6821dc97cf643ba521a4e840dda3c58b", 647410},
{"resource.005", 0, "c6e551bdc24f0acc193159038d4ca767", 605882},
{"resource.006", 0, "8f880a536908ab496bbc552f7f5c3738", 585255},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Conquests of Camelot - English DOS Non-Interactive Demo
@@ -225,7 +253,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"camelot", "Demo", {
{"resource.map", 0, "f4cd75c15be75e04cdca3acda2c0b0ea", 468},
{"resource.001", 0, "4930708722f34bfbaa4945fb08f55f61", 232523},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Conquests of Camelot - English DOS (from jvprat)
@@ -237,7 +265,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "8e1a3a8c588007404b532b8dfacc1460", 722250},
{"resource.003", 0, "8e1a3a8c588007404b532b8dfacc1460", 723712},
{"resource.004", 0, "8e1a3a8c588007404b532b8dfacc1460", 729143},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Conquests of Camelot - English DOS
@@ -251,7 +279,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "8e1a3a8c588007404b532b8dfacc1460", 345734},
{"resource.006", 0, "8e1a3a8c588007404b532b8dfacc1460", 332446},
{"resource.007", 0, "8e1a3a8c588007404b532b8dfacc1460", 358182},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Conquests of the Longbow - English Amiga (from www.back2roots.org)
@@ -266,7 +294,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "d1038c75d85a6650d48e07d174a6a913", 838175},
{"resource.005", 0, "1c3804e56b114028c5873a35c2f06d13", 653002},
{"resource.006", 0, "f9487732289a4f4966b4e34eea413325", 842817},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Conquests of the Longbow - English DOS
@@ -280,7 +308,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "9cfce07e204a329e94fda8b5657621da", 1064637},
{"resource.005", 0, "d036df0872f2db19bca34601276be2d7", 1154950},
{"resource.006", 0, "b367a6a59f29ee30dde1d88a5a41152d", 1042966},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Conquests of the Longbow - English DOS Floppy (from jvprat)
@@ -294,7 +322,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "1867136d01ece57b531032d466910522", 823686},
{"resource.004", 0, "9cfce07e204a329e94fda8b5657621da", 1261462},
{"resource.005", 0, "21ebe6b39b57a73fc449f67f013765aa", 1284720},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Conquests of the Longbow - English DOS
@@ -307,7 +335,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "1867136d01ece57b531032d466910522", 823610},
{"resource.004", 0, "9cfce07e204a329e94fda8b5657621da", 1260237},
{"resource.005", 0, "21ebe6b39b57a73fc449f67f013765aa", 1284609},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Conquests of the Longbow EGA - English DOS
@@ -321,18 +349,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "b7bb35c027bb424ecefcd122768e5e60", 705631},
{"resource.005", 0, "58942b1aa6d6ffeb66e9f8897fd4435f", 469243},
{"resource.006", 0, "8c767b3939add63d11274065e46aad04", 713158},
- {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Conquests of the Longbow - English DOS Non-Interactive Demo
// SCI interpreter version 1.000.510
{"longbow", "Demo", {
{"resource.map", 0, "cbc5cb73341de1bff1b1e20a640af220", 588},
{"resource.001", 0, "f05a20cc07eee85da8e999d0ac0f596b", 869916},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Conquests of the Longbow - German DOS (suplied by markcoolio in bug report #2727681)
+ // Conquests of the Longbow - German DOS (suplied by markcoolio in bug report #2727681, also includes english language)
// SCI interpreter version 1.000.510
{"longbow", "", {
{"resource.map", 0, "7376b7a07f8bd3a8ab8d67595d3f5b51", 6285},
@@ -343,8 +370,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "9cfce07e204a329e94fda8b5657621da", 1101869},
{"resource.005", 0, "d036df0872f2db19bca34601276be2d7", 1176914},
{"resource.006", 0, "b367a6a59f29ee30dde1d88a5a41152d", 1123585},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Eco Quest - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "x.yyy.zzz"
@@ -352,16 +379,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ecoquest", "Demo", {
{"resource.map", 0, "c819e171359b7c95f4c13b846d5c034e", 873},
{"resource.001", 0, "baf9393a9bfa73098adb501e5bc5487b", 657518},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Eco Quest - English DOS CD 1.1
// SCI interpreter version 1.001.064
{"ecoquest", "CD", {
{"resource.map", 0, "a4b73d5d2b55bdb6e44345e99c8fbdd0", 4804},
{"resource.000", 0, "d908dbef56816ac6c60dd145fdeafb2b", 3536046},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Eco Quest - English DOS Floppy
@@ -372,7 +398,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "96d4435d24c01f1c1675e46457604c5f", 1413719},
{"resource.002", 0, "28fe9b4f0567e71feb198bc9f3a2c605", 1241816},
{"resource.003", 0, "f3146df0ad4297f5ce35aa8c4753bf6c", 586832},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Eco Quest - English DOS Floppy
@@ -383,10 +409,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "2fed7451bca81b0c891eed1a956f2263", 1212161},
{"resource.002", 0, "323b3b12f43d53f27d259beb225f0aa7", 1129316},
{"resource.003", 0, "83ac03e4bddb2c1ac2d36d2a587d0536", 1145616},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Eco Quest - German DOS Floppy (supplied by markcoolio in bug report #2723744)
+ // Eco Quest - German DOS Floppy (supplied by markcoolio in bug report #2723744, also includes english language)
// SCI interpreter version 1.000.510
{"ecoquest", "Floppy", {
{"resource.map", 0, "7a9b43bf27dc000ac8559ecbe824b659", 4395},
@@ -394,10 +420,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "2fed7451bca81b0c891eed1a956f2263", 1212060},
{"resource.002", 0, "02d7d0411f7903aacb3bc8b0f8ca8a9a", 1202581},
{"resource.003", 0, "84dd11b6825255671c703aee5ceff620", 1175835},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Eco Quest - Spanish DOS Floppy (from jvprat)
+ // Eco Quest - Spanish DOS Floppy (from jvprat, also includes english language)
// Executable scanning reports "1.ECO.013", VERSION file reports "1.000, 11.12.92"
// SCI interpreter version 1.000.510
{"ecoquest", "Floppy", {
@@ -406,10 +432,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "2fed7451bca81b0c891eed1a956f2263", 1212060},
{"resource.002", 0, "2d21a1d2dcbffa551552e3e0725d2284", 1186033},
{"resource.003", 0, "84dd11b6825255671c703aee5ceff620", 1174993},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Eco Quest - French DOS Floppy (from Strangerke)
+ // Eco Quest - French DOS Floppy (from Strangerke, also includes english language)
// SCI interpreter version 1.ECO.013
{"ecoquest", "Floppy", {
{"resource.map", 0, "67742945cd59b896d9f22a549f605217", 4407},
@@ -417,15 +443,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "fc7fba54b6bb88fd7e9c229636599aa9", 1205841},
{"resource.002", 0, "b836c6ee9de67d814ac5d1b05f5b9858", 1173872},
{"resource.003", 0, "f8f767f9d6351432621c6e54c1b2ba8c", 1141520},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Eco Quest 2 - English DOS Non-Interactive Demo
// SCI interpreter version 1.001.055
{"ecoquest2", "Demo", {
{"resource.map", 0, "607cfa0d8a03b7d348c06ee727e3d939", 1321},
{"resource.000", 0, "dd6f614c43c029f063e93cd243af90a4", 525992},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Eco Quest 2 - English DOS Floppy (supplied by markcoolio in bug report #2723761)
@@ -433,7 +459,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ecoquest2", "Floppy", {
{"resource.map", 0, "28fb7b6abb9fc1cb8882d7c2e701b63f", 5658},
{"resource.000", 0, "cc1d17e5637528dbe4a812699e1cbfc6", 4208192},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Eco Quest 2 - French DOS Floppy (from Strangerke)
@@ -441,7 +467,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ecoquest2", "Floppy", {
{"resource.map", 0, "c22ab8b33c339c138b6b1697b77b9e79", 5588},
{"resource.000", 0, "1c4093f7248240329121fdf8c0d59152", 4231946},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Freddy Pharkas - English DOS demo (from FRG)
@@ -449,7 +475,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"freddypharkas", "Demo", {
{"resource.map", 0, "97aa9fcfe84c9993a64debd28c32393a", 1909},
{"resource.000", 0, "5ea8e7a3ea10cce6efd5c106dc62fd8c", 867724},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Freddy Pharkas - English CD (from FRG)
@@ -457,7 +483,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"freddypharkas", "CD", {
{"resource.map", 0, "d46b282f228a67ba13bd4b4009e95f8f", 6058},
{"resource.000", 0, "ee3c64ffff0ba9fb08bea2624631c598", 5490246},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Freddy Pharkas - English DOS Floppy (updated information from markcoolio in bug reports #2723773 and #2724720)
@@ -467,7 +493,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a32674e7fbf7b213b4a066c8037f16b6", 5816},
{"resource.000", 0, "96b07e9b914dba1c8dc6c78a176326df", 5233230},
{"resource.msg", 0, "554f65315d851184f6e38211489fdd8f", -1},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Freddy Pharkas - Windows (supplied by abevi in bug report #2612718)
@@ -476,7 +502,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"freddypharkas", "Floppy", {
{"resource.map", 0, "a32674e7fbf7b213b4a066c8037f16b6", 5816},
{"resource.000", 0, "fed4808fdb72486908ac7ad0044b14d8", 5233230},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// Freddy Pharkas - German DOS Floppy (from Tobis87, updated information from markcoolio in bug reports #2723772 and #2724720)
@@ -486,7 +512,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a32674e7fbf7b213b4a066c8037f16b6", 5816},
{"resource.000", 0, "96b07e9b914dba1c8dc6c78a176326df", 5233230},
{"resource.msg", 0, "304b5a5781800affd2235152a5794fa8", -1},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Freddy Pharkas - Spanish DOS (from jvprat)
@@ -499,7 +525,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "419dbd5366f702b4123dedbbb0cffaae", 1456640},
{"resource.003", 0, "05acdc256c742e79c50b9fe7ec2cc898", 863310},
{"resource.msg", 0, "45b5bf74933ac3727e4cc844446dc052", 796156},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
// Freddy Pharkas - Spanish DOS (from jvprat)
@@ -509,7 +535,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a32674e7fbf7b213b4a066c8037f16b6", 5816},
{"resource.000", 0, "96b07e9b914dba1c8dc6c78a176326df", 5233230},
{"resource.msg", 0, "45b5bf74933ac3727e4cc844446dc052", 796156},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Freddy Pharkas - English DOS CD Demo
@@ -517,7 +543,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"freddypharkas", "CD Demo", {
{"resource.map", 0, "a62a7eae85dd1e6b07f39662b278437e", 1918},
{"resource.000", 0, "4962a3c4dd44e36e78ea4a7a374c2220", 957382},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NONE },
// Fun Seeker's Guide - English DOS
@@ -525,7 +551,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"funseeker", "", {
{"resource.map", 0, "7ee6859ef74314f6d91938c3595348a9", 282},
{"resource.001", 0, "f1e680095424e31f7fae1255d36bacba", 40692},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Gabriel Knight - English DOS CD Demo
@@ -533,7 +559,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD Demo", {
{"resource.map", 0, "39645952ae0ed8072c7e838f31b75464", 2490},
{"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NONE },
#ifdef ENABLE_SCI32
@@ -542,16 +568,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "", {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Gabriel Knight - English DOS Floppy (supplied my markcoolio in bug report #2723777)
// SCI interpreter version 2.000.000
{"gk1", "", {
{"resource.map", 0, "65e8c14092e4c9b3b3538b7602c8c5ec", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Gabriel Knight - German DOS Floppy (supplied my markcoolio in bug report #2723775)
@@ -559,7 +584,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "", {
{"resource.map", 0, "ad6508b0296b25c07b1f58828dc33696", 10789},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13077029},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Gabriel Knight - English DOS CD (from jvprat)
@@ -567,7 +592,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD", {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Gabriel Knight - German DOS CD (from Tobis87)
@@ -575,7 +600,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD", {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE },
// Gabriel Knight - Spanish DOS CD (from jvprat)
@@ -583,7 +608,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD", {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
// Gabriel Knight - French DOS CD (from Hkz)
@@ -591,7 +616,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD", {
{"resource.map", 0, "55f909ba93a2515042a08d8a2da8414e", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13325145},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE },
// Gabriel Knight - English Windows CD (from jvprat)
@@ -599,7 +624,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD", {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
// Gabriel Knight - German Windows CD (from Tobis87)
@@ -607,7 +632,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD", {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformWindows, 0, GUIO_NONE },
// Gabriel Knight - Spanish Windows CD (from jvprat)
@@ -615,7 +640,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk1", "CD", {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformWindows, 0, GUIO_NONE },
// Gabriel Knight 2 - English Windows Non-Interactive Demo
@@ -623,7 +648,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"gk2", "Demo", {
{"resource.map", 0, "e0effce11c4908f4b91838741716c83d", 1351},
{"resource.000", 0, "d04cfc7f04b6f74d13025378be49ec2b", 4640330},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Gabriel Knight 2 - English DOS (from jvprat)
@@ -641,7 +666,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.005", 0, "14b62d4a3bddee57a03cb1495a798a0f", 38075705},
{"resmap.006", 0, "ce9359037277b7d7976da185c2fa0aad", 2977},
{"ressci.006", 0, "8e44e03890205a7be12f45aaba9644b4", 60659424},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Gabriel Knight 2 - French DOS (6-CDs Sierra Originals reedition)
@@ -659,7 +684,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.005", 0, "1eb5a72744799f5a5518543f5b4c3c79", 37882126},
{"resmap.006", 0, "11b2e722170b8c93fdaa5428e2c7676f", 3001},
{"ressci.006", 0, "4037d941aec39d2e654e20960429aefc", 60568486},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::FR_FRA, Common::kPlatformPC, 0,
GUIO_NOSPEECH },
@@ -672,7 +697,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 162783},
{"resource.002", 0, "e0dd44069a62a463fd124974b915f10d", 342309},
{"resource.003", 0, "e0dd44069a62a463fd124974b915f10d", 328912},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Hoyle 1 - English DOS (supplied by merkur in bug report #2719227)
@@ -680,7 +705,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"hoyle1", "", {
{"resource.map", 0, "1034a218943d12f1f36e753fa10c95b8", 4386},
{"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 518308},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
#if 0 // TODO: unknown if these files are corrupt
@@ -690,7 +715,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "2a72b1aba65fa6e339370eb86d8601d1", 5166},
{"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 218755},
{"resource.002", 0, "e0dd44069a62a463fd124974b915f10d", 439502},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
#endif
@@ -700,7 +725,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "4f894d203f64aa23d9ff64d30ae36926", 2100},
{"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 98138},
{"resource.002", 0, "8f2dd70abe01112eca464cda818b5eb6", 196631},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Hoyle 2 - English Amiga (from www.back2roots.org)
@@ -709,9 +734,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"hoyle2", "", {
{"resource.map", 0, "62ed48d20c580e5a98f102f7cd93706a", 1356},
{"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 222704},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+
+ // Hoyle 2 - English Macintosh
+ // Executable scanning reports "x.yyy.zzz"
+ {"hoyle2", "", {
+ {"resource.map", 0, "1af1d3aa3cf564f93477c9f87e53f495", 1728},
+ {"resource.001", 0, "b73b8131669d69d41a326415e4519138", 482882},
{NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
#if 0 // TODO: unknown if these files are corrupt
// Hoyle 3 - English Amiga (from www.back2roots.org)
@@ -721,7 +753,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "f1f158e428398cb87fc41fb4aa8c2119", 2088},
{"resource.000", 0, "595b6039ea1356e7f96a52c58eedcf22", 355791},
{"resource.001", 0, "143df8aef214a2db34c2d48190742012", 632273},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
#endif
@@ -731,7 +763,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"hoyle3", "Demo", {
{"resource.map", 0, "0d06cacc87dc21a08cd017e73036f905", 735},
{"resource.001", 0, "24db2bccda0a3c43ac4a7b5edb116c7e", 797678},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Hoyle 3 - English DOS Floppy (from jvprat)
@@ -741,7 +773,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7216a2972f9c595c45ab314941628e43", 2247},
{"resource.000", 0, "6ef28cac094dcd97fdb461662ead6f92", 541845},
{"resource.001", 0, "0a98a268ee99b92c233a0d7187c1f0fa", 845795},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Hoyle 3 - English DOS Floppy 1.0 (supplied by abevi in bug report #2612718)
@@ -749,31 +781,72 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "1728af1f6a85938c3522e64449e76ca1", 2205},
{"resource.000", 0, "6ef28cac094dcd97fdb461662ead6f92", 319905},
{"resource.001", 0, "0a98a268ee99b92c233a0d7187c1f0fa", 526438},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Hoyle 4 - English DOS Demo
+ {"hoyle4", "Demo", {
+ {"resource.map", 0, "60f764020a6b788bbbe415dbc2ccb9f3", 931},
+ {"resource.000", 0, "5fe3670e3ddcd4f85c10013b5453141a", 615522},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+
+ // Hoyle 4 - English DOS Demo
// SCI interpreter version 1.001.200 (just a guess)
+ // Does anyone have this version? -clone2727
{"hoyle4", "Demo", {
{"resource.map", 0, "662087cb383e52e3cc4ae7ecb10e20aa", 938},
{"resource.000", 0, "24c10844792c54d476d272213cbac300", 675252},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Jones in the Fast Lane - English DOS
+ // Hoyle 4 (Hoyle Classic Card Games) - English DOS/Win
+ // SCI1.1
+ // Supplied by abevi in bug report #3039291
+ {"hoyle4", "Demo", {
+ {"resource.map", 0, "2b577c975cc8d8d43f61b6a756129fe3", 4352},
+ {"resource.000", 0, "43e2c15ce436aab611a462ad0603e12d", 2000132},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Jones in the Fast Lane EGA - English DOS
+ // SCI interpreter version 1.000.172 (not 100% sure FIXME)
+ {"jones", "EGA", {
+ {"resource.map", 0, "be4cf9e8c1e253623ef35ae3b8a1d998", 1800},
+ {"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 202105},
+ {"resource.002", 0, "b86daa3ba2784d1502da881eedb80d9b", 341771},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Jones in the Fast Lane EGA - English DOS (supplied by EddyDrama in bug report #3038761)
+ {"jones", "EGA", {
+ {"resource.map", 0, "8e92cf319180cc8b5b87b2ce93a4fe22", 1602},
+ {"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 511528},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Jones in the Fast Lane VGA - English DOS
// SCI interpreter version 1.000.172
{"jones", "", {
{"resource.map", 0, "65cbe19b36fffc71c8e7b2686bd49ad7", 1800},
{"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 313476},
{"resource.002", 0, "b86daa3ba2784d1502da881eedb80d9b", 719747},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Jones in the Fast Lane VGA - English DOS (supplied by omer_mor in bug report #3037054)
+ // VERSION file reports "1.000.060"
+ {"jones", "", {
+ {"resource.map", 0, "db175ab494ab0666f19ab8f2597a8e49", 1602},
+ {"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 994487},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Jones in the Fast Lane - English DOS CD
{"jones", "CD", {
{"resource.map", 0, "459f5b04467bc2107aec02f5c4b71b37", 4878},
{"resource.001", 0, "3876da2ce16fb7dea2f5d943d946fa84", 1652150},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// King's Quest 1 SCI Remake - English Amiga (from www.back2roots.org)
@@ -785,7 +858,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "9ae2a13708d691cd42f9129173c4b39d", 795123},
{"resource.003", 0, "9ae2a13708d691cd42f9129173c4b39d", 763224},
{"resource.004", 0, "9ae2a13708d691cd42f9129173c4b39d", 820443},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// King's Quest 1 SCI Remake - English DOS Non-Interactive Demo
@@ -793,7 +866,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq1sci", "SCI Remake Demo", {
{"resource.map", 0, "59b13619078bd47011421468959ee5d4", 954},
{"resource.001", 0, "4cfb9040db152868f7cb6a1e8151c910", 296555},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// King's Quest 1 SCI Remake - English DOS (from the King's Quest Collection)
@@ -804,7 +877,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "fed9e0072ffd511d248674e60dee2099", 555439},
{"resource.002", 0, "fed9e0072ffd511d248674e60dee2099", 714062},
{"resource.003", 0, "fed9e0072ffd511d248674e60dee2099", 717478},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 4 - English Amiga (from www.back2roots.org)
@@ -817,7 +890,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "fb351106ec865fad9af5d78bd6b8e3cb", 663629},
{"resource.003", 0, "fd16c9c223f7dc5b65f06447615224ff", 683016},
{"resource.004", 0, "3fac034c7d130e055d05bc43a1f8d5f8", 549993},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// King's Quest 4 - English DOS Non-Interactive Demo
@@ -825,7 +898,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq4sci", "Demo", {
{"resource.map", 0, "992ac7cc31d3717fe53818a9bb6d1dae", 594},
{"resource.001", 0, "143e1c14f15ad0fbfc714f648a65f661", 205330},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// King's Quest 4 - English DOS (from the King's Quest Collection)
@@ -837,7 +910,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "77615c595388acf3d1df8e107bfb6b52", 536573},
{"resource.003", 0, "77615c595388acf3d1df8e107bfb6b52", 707591},
{"resource.004", 0, "77615c595388acf3d1df8e107bfb6b52", 479562},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 4 - English DOS
@@ -851,7 +924,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "851a62d00972dc4002f472cc0d84e71d", 321593},
{"resource.006", 0, "851a62d00972dc4002f472cc0d84e71d", 333777},
{"resource.007", 0, "851a62d00972dc4002f472cc0d84e71d", 341038},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 4 - English DOS
@@ -865,7 +938,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "0c8566848a76eea19a6d6220914030a7", 325102},
{"resource.006", 0, "0c8566848a76eea19a6d6220914030a7", 337288},
{"resource.007", 0, "0c8566848a76eea19a6d6220914030a7", 343882},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 5 - English Amiga (from www.back2roots.org)
@@ -881,10 +954,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "31a5487f4d942e6354d5be49d59707c9", 834146},
{"resource.006", 0, "26c0c25399b6715fec03fc3e12544fe3", 823048},
{"resource.007", 0, "b914b5901e786327213e779725d30dd1", 778772},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
- // King's Quest 5 - German Amiga
+ // King's Quest 5 - German Amiga (also includes english language)
// Executable scanning reports "1.004.024"
// SCI interpreter version 1.000.060
{"kq5", "", {
@@ -897,10 +970,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "5aa3d59968b569cd509dde00d4eb8751", 754201},
{"resource.006", 0, "56546b20db11a4836f900efa6d3a3e74", 672099},
{"resource.007", 0, "56546b20db11a4836f900efa6d3a3e74", 794194},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // King's Quest 5 - Italian Amiga
+ // King's Quest 5 - Italian Amiga (also includes english language)
// Executable scanning reports "1.004.024"
// SCI interpreter version 1.000.060
{"kq5", "", {
@@ -913,8 +986,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "de3c5c09e350fded36ca354998c2194d", 754784},
{"resource.006", 0, "11cb750f5f816445ad0f4b9f50a4f59a", 672527},
{"resource.007", 0, "11cb750f5f816445ad0f4b9f50a4f59a", 794259},
- {NULL, 0, NULL, 0}},
- Common::IT_ITA, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::IT_ITA, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// King's Quest 5 - English DOS CD (from the King's Quest Collection)
// Executable scanning reports "x.yyy.zzz", VERSION file reports "1.000.052"
@@ -923,7 +996,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "f68ba690e5920725dcf9328001b90e33", 13122},
{"resource.000", 0, "449471bfd77be52f18a3773c7f7d843d", 571368},
{"resource.001", 0, "b45a581ff8751e052c7e364f58d3617f", 16800210},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// King's Quest 5 - English DOS Floppy
@@ -938,11 +1011,27 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "b6c43441cb78a9b484efc8e614aac092", 1287999},
{"resource.006", 0, "672ede1136e9e401658538e51bd5dc22", 1172619},
{"resource.007", 0, "2f48faf27666b58c276dda20f91f4a93", 1240456},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // King's Quest 5 - English DOS Floppy (supplied by omer_mor in bug report #3036996)
+ // VERSION file reports "0.000.051"
+ {"kq5", "", {
+ {"resource.map", 0, "8b2158083302568b73b16fa3655360fe", 8184},
+ {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 276398},
+ {"resource.001", 0, "c0f48d4a7ebeaa6aa074fc98d77423e9", 1099506},
+ {"resource.002", 0, "e0c40d0e85340357d2404f9b5ae1921c", 1061243},
+ {"resource.003", 0, "89c00d788d022c13a9b250fa96290ab0", 1110169},
+ {"resource.004", 0, "d68f0d8a52ac990aa5641b7087476253", 1153751},
+ {"resource.005", 0, "ef4f1166bc37b6cfab70234ea60ddc3d", 1032675},
+ {"resource.006", 0, "06cb3f689836086ebe08b1efc0126592", 921113},
+ {"resource.007", 0, "252249753c6e850eacceb8af634986d3", 1133608},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 5 EGA (supplied by markcoolio in bug report #2829470)
// SCI interpreter version 1.000.060
+ // VERSION file reports "0.000.055"
{"kq5", "EGA", {
{"resource.map", 0, "baf888a4e4797ce0de0b19d4e183583c", 7662},
{"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 394242},
@@ -953,10 +1042,25 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "3cca5b2dae8afe94532edfdc98d7edbe", 669919},
{"resource.006", 0, "698c698570cde9015e4d51eb8d2e9db1", 666527},
{"resource.007", 0, "703d8df30e89541af337d7706540d5c4", 541743},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // King's Quest 5 - German DOS Floppy (supplied by markcoolio in bug report #2727101)
+ // King's Quest 5 EGA (supplied by omer_mor in bug report #3035421)
+ // VERSION file reports "0.000.062"
+ {"kq5", "EGA", {
+ {"resource.map", 0, "e17cfb38175382b9188da75c53bbab64", 7656},
+ {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 394072},
+ {"resource.001", 0, "c1eef048fa9fe76298c2d4705ef9549f", 561444},
+ {"resource.002", 0, "076aa0bf1d8d2c147d64aeffbe2928e5", 597580},
+ {"resource.003", 0, "ecb47cd04d06b2ab2f9f883667db6e81", 487633},
+ {"resource.004", 0, "4d74e8094ff57cea6ee92faf63dbd0af", 620749},
+ {"resource.005", 0, "3cca5b2dae8afe94532edfdc98d7edbe", 669961},
+ {"resource.006", 0, "698c698570cde9015e4d51eb8d2e9db1", 666541},
+ {"resource.007", 0, "703d8df30e89541af337d7706540d5c4", 541762},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // King's Quest 5 - German DOS Floppy (supplied by markcoolio in bug report #2727101, also includes english language)
// SCI interpreter version 1.000.060
{"kq5", "", {
{"resource.map", 0, "bff44f0c326a71b1757c793a02b502d6", 8283},
@@ -968,10 +1072,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "9c429782d102739f6bbb81e8b953b0cb", 1267525},
{"resource.006", 0, "d1a75fdc01840664d00366cff6919366", 1208972},
{"resource.007", 0, "c07494f0cce7c05210893938786a955b", 1337361},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // King's Quest 5 - French DOS Floppy (from the King's Quest Collector's Edition 1994)
+ // King's Quest 5 - French DOS Floppy (from the King's Quest Collector's Edition 1994, also includes english language)
// Supplied by aroenai in bug report #2812611
// VERSION file reports "1.000", SCI interpreter version 1.000.784
{"kq5", "", {
@@ -984,10 +1088,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "f4b31cafc5defac75125c5f7b7f9a31a", 1268334},
{"resource.006", 0, "f7dc85307632ef657ceb1651204f6f51", 1210081},
{"resource.007", 0, "7db4d0a1d8d547c0019cb7d2a6acbdd4", 1338473},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // King's Quest 5 - Italian DOS Floppy (from glorifindel)
+ // King's Quest 5 - Italian DOS Floppy (from glorifindel, includes english language)
// SCI interpreter version 1.000.060
{"kq5", "", {
{"resource.map", 0, "d55c9e83894a0885e37cd79bacf86384", 8283},
@@ -999,10 +1103,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "f4e441f284560eaa8022102315656a7d", 1267757},
{"resource.006", 0, "8eeabd92af71e766e323db2100879102", 1209325},
{"resource.007", 0, "dc10c107e0923b902326a040b9c166b9", 1337859},
- {NULL, 0, NULL, 0}},
- Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722)
+ // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722, includes english language?!)
// SCI interpreter version 1.000.060
{"kq5", "", {
{"resource.map", 0, "70010c20138541f89013bb5e1b30f16a", 7998},
@@ -1014,8 +1118,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "6556ff8e7c4d1acf6a78aea154daa76c", 1287869},
{"resource.006", 0, "da82e4beb744731d0a151f1d4922fafa", 1170456},
{"resource.007", 0, "431def14ca29cdb5e6a5e84d3f38f679", 1240176},
- {NULL, 0, NULL, 0}},
- Common::PL_POL, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::PL_POL, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// King's Quest 5 - English Macintosh
// VERSION file reports "1.000.055"
@@ -1029,9 +1133,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "432e2a58e4d496d730697db072437337", 1366732},
{"resource.006", 0, "3d22904a374c192f51e5665b74364133", 1264079},
{"resource.007", 0, "ffe17e23d5833a79f3695addfc149a56", 1361965},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
+ // King's Quest 5 - FM-Towns (supplied by abevi in bug report #3038720)
+ {"kq5", "", {
+ {"resource.map", 0, "20c7cd248ff1a349ed354568eebd972b", 12733},
+ {"resource.000", 0, "71afd220d46bde1109c58e6acc0f3a01", 469094},
+ {"resource.001", 0, "72a569f46f1abf2d9d2b1526ad3799c3", 12808839},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformFMTowns, 0, GUIO_NONE },
+
// King's Quest 6 - English DOS Non-Interactive Demo
// Executable scanning reports "1.001.055", VERSION file reports "1.000.000"
// SCI interpreter version 1.001.055
@@ -1039,7 +1151,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "f75727c00a6d884234fa2a43c951943a", 706},
{"resource.000", 0, "535b1b920441ec73f42eaa4ccfd47b89", 264116},
{"resource.msg", 0, "54d1fdc936f98c81f9e4c19e04fb1510", 8260},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// King's Quest 6 - English DOS Floppy
@@ -1048,7 +1160,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a362063318eebe7d6423b1d9dc6213e1", 8703},
{"resource.000", 0, "f2b7f753992c56a0c7a08d6a5077c895", 7863324},
{"resource.msg", 0, "3cf5de44de36191f109d425b8450efc8", 258590},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 6 - German DOS Floppy (supplied by markcoolio in bug report #2727156)
@@ -1057,7 +1169,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a362063318eebe7d6423b1d9dc6213e1", 8703},
{"resource.000", 0, "f2b7f753992c56a0c7a08d6a5077c895", 7863324},
{"resource.msg", 0, "756297b2155db9e43f621c6f6fb763c3", 282822},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 6 - English DOS CD (from the King's Quest Collection)
@@ -1066,7 +1178,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq6", "CD", {
{"resource.map", 0, "7a550ebfeae2575ca00d47703a6a774c", 9215},
{"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8376352},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// King's Quest 6 - English Windows CD (from the King's Quest Collection)
@@ -1075,7 +1187,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq6", "CD", {
{"resource.map", 0, "7a550ebfeae2575ca00d47703a6a774c", 9215},
{"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8376352},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
// King's Quest 6 - Spanish DOS CD (from jvprat)
@@ -1085,7 +1197,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a73a5ab04b8f60c4b75b946a4dccea5a", 8953},
{"resource.000", 0, "4da3ad5868a775549a7cc4f72770a58e", 8537260},
{"resource.msg", 0, "41eed2d3893e1ca6c3695deba4e9d2e8", 267102},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
// King's Quest 6 - English Macintosh Floppy
@@ -1093,7 +1205,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq6", "", {
{"Data1", 0, "f3c38a33c94293b8ff0337c1090a4973", 3916479},
{"Data2", 0, "b255edf327d7b366dce816b7debf3b94", 15046256},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
@@ -1104,7 +1216,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq7", "", {
{"resource.000", 0, "4948e4e1506f1e1c4e1d47abfa06b7f8", 204385195},
{"resource.map", 0, "40ccafb2195301504eba2e4f4f2c7f3d", 18925},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// King's Quest 7 - English Windows (from the King's Quest Collection)
@@ -1112,7 +1224,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq7", "", {
{"resource.map", 0, "2be9ab94429c721af8e05c507e048a15", 18697},
{"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 203882535},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// King's Quest 7 - English DOS (from FRG)
@@ -1120,7 +1232,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq7", "", {
{"resource.map", 0, "8676b0fbbd7362989a029fe72fea14c6", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 7 - English Windows (from FRG)
@@ -1128,7 +1240,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq7", "", {
{"resource.map", 0, "8676b0fbbd7362989a029fe72fea14c6", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// King's Quest 7 - German Windows (supplied by markcoolio in bug report #2727402)
@@ -1136,7 +1248,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq7", "", {
{"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697},
{"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 7 - Spanish DOS (from jvprat)
@@ -1144,7 +1256,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq7", "", {
{"resource.map", 0, "0b62693cbe87e3aaca3e8655a437f27f", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// King's Quest 7 - English DOS Non-Interactive Demo
@@ -1152,7 +1264,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"kq7", "Demo", {
{"resource.map", 0, "b44f774108d63faa1d021101221c5a54", 1690},
{"resource.000", 0, "d9659d2cf0c269c6a9dc776707f5bea0", 2433827},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
#endif // ENABLE_SCI32
@@ -1168,7 +1280,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "2ab23f64306b18c28302c8ec2964c5d6", 605134},
{"resource.004", 0, "aa553977f7e5804081de293800d3bcce", 695067},
{"resource.005", 0, "bfd870d51dc97729f0914095f58e6957", 676881},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Laura Bow - English Atari ST (from jvprat)
@@ -1180,7 +1292,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 721149},
{"resource.003", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 667365},
{"resource.004", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 683737},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO_NOSPEECH },
// Laura Bow - English DOS Non-Interactive Demo
@@ -1188,7 +1300,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"laurabow", "Demo", {
{"resource.map", 0, "e625726268ff4e123ada11f31f0249f3", 768},
{"resource.001", 0, "0c8912290af0890f8d95faeb4ddb2d68", 333031},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Laura Bow - English DOS 3.5" Floppy (from "The Roberta Williams Anthology"/1996)
@@ -1199,7 +1311,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 721381},
{"resource.003", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 667468},
{"resource.004", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 683807},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Laura Bow - English DOS (from FRG)
@@ -1213,10 +1325,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 327465},
{"resource.006", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 328390},
{"resource.007", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 317687},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Laura Bow - German DOS (from Tobis87)
+ // Laura Bow - German DOS (from Tobis87, also includes english language)
// SCI interpreter version 0.000.631 (or 0.000.685?)
{"laurabow", "", {
{"resource.map", 0, "b1905f6aa68ff65a057b080b1eae954c", 12030},
@@ -1227,8 +1339,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 327465},
{"resource.006", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 328390},
{"resource.007", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 317687},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Laura Bow 2 - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "x.yyy.zzz"
@@ -1236,7 +1348,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"laurabow2", "Demo", {
{"resource.map", 0, "24dffc5db1d88c7999f13e8767ed7346", 855},
{"resource.000", 0, "2b2b1b4f7584f9b38fd13f6ab95634d1", 781912},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Laura Bow 2 - English DOS Floppy
@@ -1245,7 +1357,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"laurabow2", "", {
{"resource.map", 0, "610bfd9a852004222f0faaf5fc9e630a", 6489},
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5035964},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Laura Bow 2 - English DOS CD (from "The Roberta Williams Antology"/1996)
@@ -1254,7 +1366,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"laurabow2", "CD", {
{"resource.map", 0, "a70945e61ba7ac7bfea6b7bd72c6aec5", 7274},
{"resource.000", 0, "82578b8d5a7e09c4c58891ca49fae35b", 5598672},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Laura Bow 2 v1.1 - French DOS Floppy (from Hkz)
@@ -1262,7 +1374,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "3b6dfbcda210bbc3f23fd1927113bf98", 6483},
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766},
{"resource.msg", 0, "0fceedfbdd85a4bc7851fdd9dd2d2f19", 278253},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Laura Bow 2 v1.1 - German DOS Floppy (from Tobis87, updated info from markcoolio in bug report #2723787, updated info from #2797962))
@@ -1271,7 +1383,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "3b6dfbcda210bbc3f23fd1927113bf98", 6483},
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766},
{"resource.msg", 0, "795c928cd00dfec9fbc62ebcd12e1f65", 303185},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Laura Bow 2 - Spanish DOS CD (from jvprat)
@@ -1280,7 +1392,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "3b6dfbcda210bbc3f23fd1927113bf98", 6483},
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766},
{"resource.msg", 0, "71f1f0cd9f082da2e750c793a8ed9d84", 286141},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 1 EGA Remake - English DOS (from spookypeanut)
@@ -1291,7 +1403,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "38936d3c68b6f79d3ffb13955713fed7", 591352},
{"resource.002", 0, "24c958bc922b07f91e25e8c93aa01fcf", 491230},
{"resource.003", 0, "685cd6c1e05a695ab1e0db826337ee2a", 553279},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 1 VGA Remake - English Amiga (from www.back2roots.org)
@@ -1303,7 +1415,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "24ed6dc01b1e7fbc66c3d63a5994549a", 750465},
{"resource.002", 0, "5790ac0505f7ca98d4567132b875eb1e", 681041},
{"resource.003", 0, "4a34c3367c2fe7eb380d741374da1989", 572251},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Larry 1 VGA Remake - English DOS (from spookypeanut)
@@ -1313,7 +1425,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "d3bceaebef3f7be941c2038b3565161e", 922406},
{"resource.001", 0, "ec20246209d7b19f38989261e5c8f5b8", 1111226},
{"resource.002", 0, "85d6935ef77e6b0e16bc307640a0d913", 1088312},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 1 VGA Remake - English DOS (from FRG)
@@ -1323,7 +1435,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "d3bceaebef3f7be941c2038b3565161e", 918242},
{"resource.001", 0, "d34cadb11e1aefbb497cf91bc1d3baa7", 1114688},
{"resource.002", 0, "85b030bb66d5342b0a068f1208c431a8", 1078443},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 1 VGA Remake - English DOS Non-Interactive Demo
@@ -1331,10 +1443,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl1sci", "VGA Remake, Demo", {
{"resource.map", 0, "434e1f6c39d71647b34f0ee57b2bbd68", 444},
{"resource.001", 0, "0c0768215c562d9dace4a5ca53696cf3", 359913},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Larry 1 VGA Remake - Spanish DOS (from the Leisure Suit Larry Collection)
+ // Larry 1 VGA Remake - Spanish DOS (from the Leisure Suit Larry Collection, also includes english language)
// Executable scanning reports "1.SQ4.057", VERSION file reports "1.000"
// This version is known to be corrupted
// SCI interpreter version 1.000.510
@@ -1344,10 +1456,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "112648995dbc194037f1e4ed2e195910", 1063341},
{"resource.002", 0, "3fe2a3aec0ed53c7d6db1845a67e3aa2", 1095908},
{"resource.003", 0, "ac175df0ea9a2cba57f0248651856d27", 376556},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Larry 1 VGA Remake - Russian DOS
+ // Larry 1 VGA Remake - Russian DOS (also includes english language?!)
// Executable scanning reports "1.000.510", VERSION file reports "2.0"
// SCI interpreter version 1.000.510
{"lsl1sci", "VGA Remake", {
@@ -1355,8 +1467,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "0d7b2afa666bd36d9535a15d3a837a66", 928566},
{"resource.001", 0, "bc8ca10c807515d959cbd91f9ba47735", 1123759},
{"resource.002", 0, "b7409ab32bc3bee2d6cce887cd33f2b6", 1092160},
- {NULL, 0, NULL, 0}},
- Common::RU_RUS, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::RU_RUS, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
+
+ // Larry 1 VGA Remake - Polish DOS (from Polish Leisure Suit Larry Collection, official release)
+ // SCI interpreter version 1.000.577, VERSION file reports "2.1" (this release does NOT include english text)
+ {"lsl1sci", "VGA Remake", {
+ {"resource.map", 0, "58330a85767e42a2487129913283ab5b", 3228},
+ {"resource.000", 0, "b6097ff35cdc8469f02150fe2f824198", 4781210},
+ AD_LISTEND},
+ Common::PL_POL, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 2 - English Amiga (from www.back2roots.org)
// Executable scanning reports "x.yyy.zzz"
@@ -1367,7 +1487,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "a0d4a625311d307257da7fc43d00459d", 630106},
{"resource.003", 0, "a0d4a625311d307257da7fc43d00459d", 570356},
{"resource.004", 0, "a0d4a625311d307257da7fc43d00459d", 717844},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Larry 2 - English DOS Non-Interactive Demo
@@ -1376,7 +1496,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl2", "Demo", {
{"resource.map", 0, "03dba704bb77da55a91ad27b5a3cac09", 528},
{"resource.001", 0, "9f5520f0297206928df0b0b36493cd33", 127532},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Larry 2 - English DOS
@@ -1389,7 +1509,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "4a24443a25e2b1492462a52809605dc2", 204861},
{"resource.005", 0, "4a24443a25e2b1492462a52809605dc2", 277732},
{"resource.006", 0, "4a24443a25e2b1492462a52809605dc2", 345683},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 2 - English DOS
@@ -1404,7 +1524,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// TODO/FIXME: is the version with size 208739 corrupted?
//{"resource.006", 0, "96033f57accfca903750413fd09193c8", 345818},
{"resource.006", 0, "96033f57accfca903750413fd09193c8", -1}, // 345818 or 208739
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 3 - English Amiga (from www.back2roots.org)
@@ -1418,7 +1538,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "5c10e462c8cf589610773e4fe8bfd996", 527238},
{"resource.004", 0, "f408e59cbee1457f042e5773b8c53951", 651634},
{"resource.005", 0, "433911eb764089d493aed1f958a5615a", 524259},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Larry 3 - English DOS
@@ -1429,7 +1549,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "f18441027154292836b973c655fa3175", 578024},
{"resource.003", 0, "f18441027154292836b973c655fa3175", 506807},
{"resource.004", 0, "f18441027154292836b973c655fa3175", 513651},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 3 - English DOS
@@ -1443,7 +1563,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "f18441027154292836b973c655fa3175", 302946},
{"resource.006", 0, "f18441027154292836b973c655fa3175", 282465},
{"resource.007", 0, "f18441027154292836b973c655fa3175", 257174},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 3 - English DOS Non-Interactive Demo
@@ -1452,10 +1572,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "33a2384f395470af3d2180e37ad0322a", 1140},
{"resource.001", 0, "f773d79b93dfd4052ec8c1cc64c1e6ab", 76525},
{"resource.002", 0, "f773d79b93dfd4052ec8c1cc64c1e6ab", 268299},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Larry 3 - German DOS (from Tobis87, updated info from markcoolio in bug report #2723832)
+ // Larry 3 - German DOS (from Tobis87, updated info from markcoolio in bug report #2723832, also includes english language)
// Executable scanning reports "S.old.123"
// SCI interpreter version 0.000.572 (just a guess)
{"lsl3", "", {
@@ -1464,10 +1584,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "3827a9b17b926e12dcc336860f50612a", 672403},
{"resource.003", 0, "3827a9b17b926e12dcc336860f50612a", 587036},
{"resource.004", 0, "3827a9b17b926e12dcc336860f50612a", 691932},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Larry 3 - French DOS (provided by richiefs in bug report #2670691)
+ // Larry 3 - French DOS (provided by richiefs in bug report #2670691, also includes english language)
// Executable scanning reports "S.old.123"
// SCI interpreter version 0.000.572 (just a guess)
{"lsl3", "", {
@@ -1476,8 +1596,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "65f1bdaa20f6d0470e9d969f22473873", 671614},
{"resource.003", 0, "65f1bdaa20f6d0470e9d969f22473873", 586921},
{"resource.004", 0, "65f1bdaa20f6d0470e9d969f22473873", 690826},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Larry 5 - English Amiga
// Executable scanning reports "1.004.023"
@@ -1491,10 +1611,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "3ce5901f1bc171ac0274d99a4eeb9e57", 623022},
{"resource.005", 0, "f8b2d1137bb767e5d232056b99dd69eb", 623621},
{"resource.006", 0, "bafc64e3144f115dc58c6aee02de98fb", 715598},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
- // Larry 5 - German Amiga
+ // Larry 5 - German Amiga (also includes english language)
// Executable scanning reports "1.004.024"
// SCI interpreter version 1.000.784
{"lsl5", "", {
@@ -1507,15 +1627,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "59eba83ad465b08d763b44f86afa86f6", 664717},
{"resource.006", 0, "bafc64e3144f115dc58c6aee02de98fb", 754966},
{"resource.007", 0, "59eba83ad465b08d763b44f86afa86f6", 683135},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Larry 5 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.000.181
{"lsl5", "Demo", {
{"resource.map", 0, "efe8d3f45ce4f6bd9a6643e0ac8d2a97", 504},
{"resource.001", 0, "8bd8d9c0b5f455ee1269d63ce86c50dd", 531380},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Larry 5 - English DOS (from spookypeanut)
@@ -1530,11 +1650,11 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "0cc8d35a744031c772ca7cd21ae95273", 1011944},
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1024810},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 1030656},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 5 - German DOS (from Tobis87)
- // SCI interpreter version 1.000.510 (just a guess)
+ // SCI interpreter version T.A00.196
{"lsl5", "", {
{"resource.map", 0, "c97297aa76d4dd2ed144c7b7769e2caf", 6867},
{"resource.000", 0, "4c00c14b8181ad47076a51d86097d97e", 759095},
@@ -1545,8 +1665,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "0cc8d35a744031c772ca7cd21ae95273", 959342},
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1021774},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 993408},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Larry 5 - French DOS (provided by richiefs in bug report #2670691)
// Executable scanning reports "1.lsl5.019"
@@ -1561,8 +1681,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "0cc8d35a744031c772ca7cd21ae95273", 920524},
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 946540},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 958842},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Larry 5 - Spanish DOS (from the Leisure Suit Larry Collection)
// Executable scanning reports "1.ls5.006", VERSION file reports "1.000, 4/21/92"
@@ -1577,15 +1697,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "0cc8d35a744031c772ca7cd21ae95273", 958079},
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1015136},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 987222},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Larry 5 - Italian DOS Floppy (from glorifindel)
// SCI interpreter version 1.000.510 (just a guess)
{"lsl5", "", {
{"resource.map", 0, "a99776df795127f387cb35dae872d4e4", 5919},
{"resource.000", 0, "a8989a5a89e7d4f702b26b378c7a357a", 7001981},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 6 - English DOS (from spookypeanut)
@@ -1593,7 +1713,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl6", "", {
{"resource.map", 0, "bb8a39d9e2a77ba449a1e591109ad9a8", 6973},
{"resource.000", 0, "4462fe48c7452d98fddcec327a3e738d", 5789138},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 6 - English/German/French DOS CD - LORES
@@ -1601,7 +1721,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl6", "", {
{"resource.map", 0, "0b91234b7112782962cb480b7791b6e2", 7263},
{"resource.000", 0, "57d5fe8bb9e044158514476ea7678eb0", 5754790},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 6 - German DOS CD - LORES (provided by richiefs in bug report #2670691)
@@ -1609,7 +1729,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl6", "", {
{"resource.map", 0, "bafe85f32738854135991d4324ad147e", 7268},
{"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5773160},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 6 - French DOS CD - LORES (provided by richiefs in bug report #2670691)
@@ -1617,7 +1737,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl6", "", {
{"resource.map", 0, "97797ea775baaf18a1907d357d3c0ea6", 7268},
{"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5776092},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 6 - Spanish DOS - LORES (from the Leisure Suit Larry Collection)
@@ -1625,7 +1745,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl6", "", {
{"resource.map", 0, "633bf8f42170b6271019917c8009989b", 6943},
{"resource.000", 0, "7884a8db9253e29e6b37a2651fd90ba3", 5733116},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Crazy Nick's Software Picks: Leisure Suit Larry's Casino - English DOS (from the Leisure Suit Larry Collection)
@@ -1633,69 +1753,77 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"cnick-lsl", "", {
{"resource.map", 0, "194f1578f2624db813c9072359ad1639", 783},
{"resource.001", 0, "3733433b517ec3d14a3331d9ab3842ae", 344830},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Crazy Nick's Software Picks: King Graham's Board Game Challenge
{"cnick-kq", "", {
{"resource.map", 0, "44bc538a5cd24b39ffccc967c0ebf84d", 1137},
{"resource.001", 0, "470e7a4a3504635e70b623c44461e1ac", 451272},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Crazy Nick's Software Picks: Parlor Games with Laura Bow
{"cnick-laurabow", "", {
{"resource.map", 0, "3b826bfe64f8ff1ccf30eef93cd2f727", 999},
{"resource.001", 0, "985ac8db6f636f2b4334c04b0fbb44fb", 336698},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Crazy Nick's Software Picks: Robin Hood's Game of Skill and Chance
{"cnick-longbow", "", {
{"resource.map", 0, "4a5c81f485a2416bde12978506f2fb5f", 897},
{"resource.001", 0, "ef16dc9e867eb8eeb5b13e110b90bd4b", 571466},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Crazy Nick's Software Picks: Roger Wilco's Spaced Out Game Pack
{"cnick-sq", "", {
{"resource.map", 0, "b4d95b02d84e297441bd999d34eaa6b1", 879},
{"resource.001", 0, "82ff2b64a60117886fbcd6a3a8c977c6", 364921},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
// Larry 6 - English/German DOS CD - HIRES
// SCI interpreter version 2.100.002
- {"lsl6", "", {
+ {"lsl6hires", "", {
{"resource.map", 0, "0c0804434ea62278dd15032b1947426c", 8872},
{"resource.000", 0, "9a9f4870504444cda863dd14d077a680", 18520872},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 6 - German DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
- {"lsl6", "", {
+ {"lsl6hires", "", {
{"resource.map", 0, "badfdf446ffed569a310d2c63a249421", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18534274},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 6 - French DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
- {"lsl6", "", {
+ {"lsl6hires", "", {
{"resource.map", 0, "d184e9aa4f2d4b5670ddb3669db82cda", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18538987},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE },
+ // Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691)
+ // SCI interpreter version 2.100.002
+ {"lsl7", "Demo", {
+ {"ressci.000", 0, "5cc6159688b2dc03790a67c90ccc67f9", 10195878},
+ {"resmap.000", 0, "6a2b2811eef82e87cde91cf1de845af8", 2695},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+
#ifdef ENABLE_SCI3_GAMES
// Larry 7 - English DOS CD (from spookypeanut)
// SCI interpreter version 3.000.000
{"lsl7", "", {
{"resmap.000", 0, "eae93e1b1d1ccc58b4691c371281c95d", 8188},
{"ressci.000", 0, "89353723488219e25589165d73ed663e", 66965678},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 7 - German DOS (from Tobis87)
@@ -1703,7 +1831,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl7", "", {
{"resmap.000", 0, "c11e6bfcfc2f2d05da47e5a7df3e9b1a", 8188},
{"ressci.000", 0, "a8c6817bb94f332ff498a71c8b47f893", 66971724},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 7 - French DOS (provided by richiefs in bug report #2670691)
@@ -1711,7 +1839,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl7", "", {
{"resmap.000", 0, "4407849fd52fe3efb0c30fba60cd5cd4", 8206},
{"ressci.000", 0, "dc37c3055fffbefb494ff22b145d377b", 66964472},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Larry 7 - Italian DOS CD (from glorifindel)
@@ -1719,7 +1847,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl7", "", {
{"resmap.000", 0, "9852a97141f789413f29bf956052acdb", 8212},
{"ressci.000", 0, "440b9fed89590abb4e4386ed6f948ee2", 67140181},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NONE },
// Larry 7 - Spanish DOS (from the Leisure Suit Larry Collection)
@@ -1727,31 +1855,25 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"lsl7", "", {
{"resmap.000", 0, "8f3d603e1acc834a5d598b30cdfc93f3", 8188},
{"ressci.000", 0, "32792f9bc1bf3633a88b382bb3f6e40d", 67071418},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
-
- // Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691)
- // SCI interpreter version 2.100.002
- {"lsl7", "Demo", {
- {"ressci.000", 0, "5cc6159688b2dc03790a67c90ccc67f9", 10195878},
- {"resmap.000", 0, "6a2b2811eef82e87cde91cf1de845af8", 2695},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+#endif
// Lighthouse - English Windows Demo (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.00"
{"lighthouse", "Demo", {
{"resource.map", 0, "543124606352bfa5e07696ddf2a669be", 64},
{"resource.000", 0, "5d7714416b612463d750fb9c5690c859", 28952},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+#ifdef ENABLE_SCI3_GAMES
// Lighthouse - English Windows Demo
// Executable scanning reports "3.000.000", VERSION file reports "1.00"
{"lighthouse", "Demo", {
{"resmap.000", 0, "3bdee7a16926975a4729f75cf6b80a92", 1525},
{"ressci.000", 0, "3c585827fa4a82f4c04a56a0bc52ccee", 11494351},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Lighthouse - English DOS (from jvprat)
@@ -1761,7 +1883,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.001", 0, "14e922c47b92156377cb49e241691792", 99591924},
{"resmap.002", 0, "c68db5333f152fea6ca2dfc75cad8b34", 7573},
{"ressci.002", 0, "175468431a979b9f317c294ce3bc1430", 94628315},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Lighthouse - Spanish DOS (from jvprat)
@@ -1771,7 +1893,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.001", 0, "18553177dbf83fb2cb6c8edcbb174183", 99543093},
{"resmap.002", 0, "e7dc85884a2417e2eff9de0c63dd65fa", 7630},
{"ressci.002", 0, "3c8d627c555b0e3e4f1d9955bc0f0df4", 94631127},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
#endif // ENABLE_SCI3_GAMES
@@ -1782,9 +1904,20 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "c2cf672c3f4251e7472d4542af3bf764", 933},
{"resource.000", 0, "8be56a3a88c065ee00c02c0e29199f3a", 14643},
{"resource.001", 0, "9e33566515b18bee7915db448063bba2", 871853},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+ // Mixed-Up Fairy Tales - English DOS Floppy EGA (from omer_mor, bug report #3035350)
+ {"fairytales", "EGA", {
+ {"resource.map", 0, "daa94e9f327be6657eb97a51b490dbb1", 3219},
+ {"resource.000", 0, "6dc287611e510793b72e73110bbdd45d", 17819},
+ {"resource.001", 0, "5ad26e7af4d4c3a3185c66a44abd5220", 478401},
+ {"resource.002", 0, "4db83250f821607b634c99d663cae74a", 663713},
+ {"resource.003", 0, "509b2467ba779100d5933ed51a9ae32f", 560255},
+ {"resource.004", 0, "93afc85d5ffa60ea555d6cc336d22c03", 651109},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Mixed-Up Fairy Tales v1.000 - English DOS (supplied by markcoolio in bug report #2723791)
// Executable scanning reports "1.000.145"
{"fairytales", "", {
@@ -1794,7 +1927,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "6767f8c8585f617aaa91d442f41ae714", 1032989},
{"resource.003", 0, "b1288e0821ee358d1ffe877e5900c8ec", 1047565},
{"resource.004", 0, "f79daa70390d73746742ffcfc3dc4471", 937580},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Mixed-Up Fairy Tales - English DOS Floppy (from jvprat)
@@ -1805,7 +1938,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "49c8f7dcd9989e4491a93554bec325b0", 238019},
{"resource.002", 0, "564f516d991032e781492592a4eaa275", 1414142},
{"resource.003", 0, "dd6cef0c592eadb7e6be9a25307c57a2", 1344719},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Mixed-Up Mother Goose - English Amiga (from www.back2roots.org)
@@ -1815,15 +1948,23 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "4aa28ac93fae03cf854594da13d9229c", 2700},
{"resource.001", 0, "fb552ae550ca1dac19ed8f6a3767612d", 262885},
{"resource.002", 0, "fb552ae550ca1dac19ed8f6a3767612d", 817191},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+ // Mixed-Up Mother Goose - English DOS Floppy EGA (from omer_mor, bug report #3035354)
+ {"mothergoose", "EGA", {
+ {"resource.map", 0, "3490f85dab47e504c41b7eb3312e285e", 2598},
+ {"resource.001", 0, "d893892d62b3f061357291d66775e360", 239906},
+ {"resource.002", 0, "d893892d62b3f061357291d66775e360", 719398},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Mixed-Up Mother Goose v2.000 - English DOS Floppy (supplied by markcoolio in bug report #2723795)
// Executable scanning reports "1.001.031"
{"mothergoose", "", {
{"resource.map", 0, "52aae15e493cafd1da7e1c9b657a5bb9", 7026},
{"resource.000", 0, "b7ecd8ae9e254e80310b5a668b276e6e", 2948975},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Mixed-Up Mother Goose - English DOS CD (from jvprat)
@@ -1832,7 +1973,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"mothergoose", "CD", {
{"resource.map", 0, "1c7f311b0a2c927b2fbe81ae341fb2f6", 5790},
{"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 4369438},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Mixed-Up Mother Goose - English Windows Interactive Demo
@@ -1840,17 +1981,31 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"mothergoose", "Demo", {
{"resource.map", 0, "87f9dc1cafc4d4fa835fb2f00cf3a6ef", 4560},
{"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 2070072},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NOSPEECH },
+
+ // Mixed-Up Mother Goose - FM-Towns (supplied by abevi in bug report #3038720)
+ {"mothergoose", "", {
+ {"resource.map", 0, "b11e971ccd2040bebba59dfb409a08ef", 5772},
+ {"resource.001", 0, "d49625d9b8005ec01c852f8322a82867", 4330713},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformFMTowns, 0, GUIO_NONE },
#ifdef ENABLE_SCI32
// Mixed-Up Mother Goose Deluxe - English Windows/DOS CD (supplied by markcoolio in bug report #2723810)
// Executable scanning reports "2.100.002"
- {"mothergoose", "", {
+ {"mothergoosehires", "", {
{"resource.map", 0, "5159a1578c4306bfe070a3e4d8c2e1d3", 4741},
{"resource.000", 0, "1926925c95d82f0999590e93b02887c5", 15150768},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+
+ // Mixed-Up Mother Goose Deluxe - Multilingual Windows CD (English/French/German/Spanish)
+ // Executable scanning reports "2.100.002"
+ {"mothergoosehires", "", {
+ {"resmap.000", 0, "ef611af561898dcfea87846919ebf3eb", 4969},
+ {"ressci.000", 0, "227685bc59d90821978d330713e44a7a", 17205800},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
#endif // ENABLE_SCI32
@@ -1859,7 +2014,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"msastrochicken", "", {
{"resource.map", 0, "5b457cbe5042f557e5b610148171f6c0", 1158},
{"resource.001", 0, "453ea81ef66a50cbe33ce06302afe47f", 229737},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
@@ -1880,7 +2035,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.006", 0, "3aae6559aa1df273bc542d5ac6330d75", 77901360},
{"resmap.007", 0, "afbd16ea77869a720afa1c5371de107d", 7972},
//{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 25859038},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Phantasmagoria - English DOS Demo
@@ -1888,7 +2043,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"phantasmagoria", "Demo", {
{"resmap.001", 0, "416138651ea828219ca454cae18341a3", 11518},
{"ressci.001", 0, "3aae6559aa1df273bc542d5ac6330d75", 65844612},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
#ifdef ENABLE_SCI3_GAMES
@@ -1905,7 +2060,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.004", 0, "53f457cddb0dffc056593905c4cbb989", 42447131},
{"resmap.005", 0, "8bd5ceeedcbe16dfe55d1b90dcd4be84", 1942},
{"ressci.005", 0, "05f9fe2bee749659acb3cd2c90252fc5", 67905112},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
#endif // ENABLE_SCI3_GAMES
@@ -1916,7 +2071,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"pepper", "", {
{"resource.map", 0, "72726dc81c1b4c1110c486be77369bc8", 5179},
{"resource.000", 0, "670d0c53622429f4b11275caf7f8d292", 5459574},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Pepper - English DOS Non-Interactive Demo
@@ -1924,7 +2079,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"pepper", "Demo", {
{"resource.map", 0, "379bb4fb896630b14f2d91ed21e36ba1", 984},
{"resource.000", 0, "118f6c31a93ec7fd9a231c61125229e3", 645494},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Pepper - English DOS/Windows Interactive Demo
@@ -1932,7 +2087,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"pepper", "Demo", {
{"resource.map", 0, "975e8df76106a5c13d12ab674f906a02", 2514},
{"resource.000", 0, "e6a918a2dd7a4bcecd8fb389f43287c2", 1698164},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Pepper - English DOS Interactive Demo
@@ -1940,7 +2095,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"pepper", "Demo", {
{"resource.map", 0, "9c9b7b900651a370dd3fb38d478b1798", 2524},
{"resource.000", 0, "e6a918a2dd7a4bcecd8fb389f43287c2", 1713544},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Police Quest 1 VGA Remake - English DOS (from the Police Quest Collection)
@@ -1948,7 +2103,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"pq1sci", "VGA Remake", {
{"resource.map", 0, "35efa814fb994b1cbdac9611e401da67", 5013},
{"resource.000", 0, "e0d5ddf34eda903a38f0837e2aa7145b", 6401433},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest 2 - English Amiga (from www.back2roots.org)
@@ -1959,7 +2114,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "523db0c07f1da2a822c2c39ee0482544", 179334},
{"resource.002", 0, "499737c21a28ac026e11ab817100d610", 511099},
{"resource.003", 0, "e008f5d6e2a7c4d4a0da0173e4fa8f8b", 553970},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Police Quest 2 - English DOS Non-Interactive Demo
@@ -1967,7 +2122,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"pq2", "Demo", {
{"resource.map", 0, "8b77d0d4650c2052b356cece28294b58", 576},
{"resource.001", 0, "376ef6d6eaaeed66e1424bd219c4b9ab", 215398},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Police Quest 2 - English DOS (provided by richiefs in bug report #2670691)
@@ -1980,7 +2135,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "77f02def3094af804fd2371db25b7100", 342149},
{"resource.005", 0, "77f02def3094af804fd2371db25b7100", 349899},
{"resource.006", 0, "77f02def3094af804fd2371db25b7100", 354991},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest 2 - English DOS (from the Police Quest Collection)
@@ -1990,7 +2145,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "77f02def3094af804fd2371db25b7100", 509525},
{"resource.002", 0, "77f02def3094af804fd2371db25b7100", 546000},
{"resource.003", 0, "77f02def3094af804fd2371db25b7100", 591851},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest 2 - English DOS (from FRG)
@@ -2000,9 +2155,28 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "77f02def3094af804fd2371db25b7100", 509760},
{"resource.002", 0, "77f02def3094af804fd2371db25b7100", 542897},
{"resource.003", 0, "77f02def3094af804fd2371db25b7100", 586857},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Police Quest 2 English DOS 1.001.006 (supplied by merkur-kun in bug report #3028479)
+ {"pq2", "", {
+ {"resource.map", 0, "8e1161c684b342742d30f938a4839a4b", 4518},
+ {"resource.001", 0, "77f02def3094af804fd2371db25b7100", 506563},
+ {"resource.002", 0, "77f02def3094af804fd2371db25b7100", 541261},
+ {"resource.003", 0, "77f02def3094af804fd2371db25b7100", 587511},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Police Quest 2 - Japanese PC-98 (also includes english language)
+ // SCI interpreter version unknown
+ {"pq2", "", {
+ {"resource.map", 0, "883804c616dca1d82373bf9fda3a71d2", 4656},
+ {"resource.001", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 669319},
+ {"resource.002", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 637662},
+ {"resource.003", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 684395},
+ AD_LISTEND},
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO_NOSPEECH },
+
// Police Quest 3 - English Amiga
// Executable scanning reports "1.004.024"
// SCI interpreter version 1.000.784
@@ -2013,10 +2187,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "f7044bb08a1fcbe5077791ed8d4996f0", 691207},
{"resource.003", 0, "630bfa65beb05f743552704ac2899dae", 759891},
{"resource.004", 0, "7b229fbdf30d670d0728cede3e984a7e", 838663},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
- // Police Quest 3 - German Amiga
+ // Police Quest 3 - German Amiga (also includes english language)
// Executable scanning reports "1.004.024"
// SCI interpreter version 1.000.784
{"pq3", "", {
@@ -2027,9 +2201,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "87361c17fd863b58f98828de68770279", 682288},
{"resource.004", 0, "6258d5dd85898d8e218eb8113ebc9059", 722738},
{"resource.005", 0, "6258d5dd85898d8e218eb8113ebc9059", 704485},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Police Quest 3 - English DOS (from the Police Quest Collection)
// Executable scanning reports "T.A00.178", VERSION file reports "1.00"
@@ -2041,9 +2214,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "c18e0d408e4f4f40365d42aa15931f67", 1153561},
{"resource.003", 0, "8791b9eef53edf77c2dac950142221d3", 1159791},
{"resource.004", 0, "1b91e891a3c60a941dac0eecdf83375b", 1143606},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest 3 - English DOS Non-Interactive Demo
// Executable scanning reports "T.A00.052"
@@ -2052,11 +2224,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ec8e58e7663ae5173853abf6c76b52bb", 867},
{"resource.000", 0, "277f97771f7a6d89677141f02da313d6", 65150},
{"resource.001", 0, "5c5a551b6c86cce2ee75becb90e0b586", 624411},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Police Quest 3 - German DOS (supplied by markcoolio in bug report #2723837)
+ // Police Quest 3 - German DOS (supplied by markcoolio in bug report #2723837, also includes english language)
// Executable scanning reports "T.A00.178"
// SCI interpreter version 1.000.510
{"pq3", "", {
@@ -2066,18 +2237,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "cce99b96a578b62ff6cebdae8d122feb", 1179358},
{"resource.003", 0, "4836f460f4cfc8de61e2df4c45775504", 1180956},
{"resource.004", 0, "0c3eb84b9755852d9e795e0d5c9373c7", 1171760},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Police Quest 4 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.096
{"pq4", "Demo", {
{"resource.map", 0, "be56f87a1c4a13062a30a362df860c2f", 1472},
{"resource.000", 0, "527d5684016e6816157cd15d9071b11b", 1121310},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
// Police Quest 4 - English DOS (from the Police Quest Collection)
@@ -2085,45 +2254,40 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"pq4", "", {
{"resource.map", 0, "379dfe80ed6bd16c47e4b950c4722eac", 11374},
{"resource.000", 0, "fd316a09b628b7032248139003369022", 18841068},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest 4 - English DOS
// SCI interpreter version 2.000.000 (a guess?)
{"pq4", "", {
{"resource.map", 0, "aed9643158ccf01b71f359db33137f82", 9895},
{"resource.000", 0, "da383857b3be1e4514daeba2524359e0", 15141432},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest 4 - French DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 2.000.000
{"pq4", "", {
{"resource.map", 0, "008030846edcc7c5c7a812c7f4ae4ceb", 9256},
{"resource.000", 0, "6ba98bd2e436739d87ecd2a9b99cabb4", 14730153},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest 4 - German DOS (supplied by markcoolio in bug report #2723840)
// SCI interpreter version 2.000.000 (a guess?)
{"pq4", "", {
{"resource.map", 0, "2393ee728ab930b2762cb5889f9b5aff", 9256},
{"resource.000", 0, "6ba98bd2e436739d87ecd2a9b99cabb4", 14730155},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Police Quest: SWAT - English DOS/Windows Demo (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "0.001.200"
{"pqswat", "Demo", {
{"resource.map", 0, "8c96733ef94c21526792f7ca4e3f2120", 1648},
{"resource.000", 0, "d8892f1b8c56c8f7704325460f49b300", 3676175},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Police Quest: SWAT - English Windows (from the Police Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0c"
@@ -2137,9 +2301,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.003", 0, "00a755e917c442ca8cf1a1bea689e6fb", 45073980},
{"resmap.004", 0, "4228038906f041623e65789500b22285", 6835},
{"ressci.004", 0, "b7e619e6ecf62fe65d5116a3a422e5f0", 46223872},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
#endif // ENABLE_SCI32
// Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by merkur in bug report #2718784)
@@ -2151,9 +2314,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "439ba9b6dde216e6eb97ef3a9830fbe4", 646869},
{"resource.003", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 642203},
{"resource.004", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 641688},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by markcoolio in bug report #2723843)
// Executable scanning reports "0.000.566"
@@ -2167,31 +2329,36 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "7288ed6d5da89b7a80b4af3897a7963a", 271185},
{"resource.006", 0, "69366c2a2f99917199fe1b60a4fee19d", 267852},
{"resource.007", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 272747},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Quest for Glory 1 / Hero's Quest - English DOS Demo
+ // Executable scanning reports "0.000.685"
+ {"qfg1", "Demo", {
+ {"resource.map", 0, "df34c758cbb9026da175793ff686b0e6", 882},
+ {"resource.001", 0, "73fbaafdd313b39aeedb80fbf85ecef1", 389884},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Quest for Glory 1 - Japanese PC-98 5.25" Floppy
+ // Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes english language)
// Executable scanning reports "S.old.201"
{"qfg1", "8 Colors", {
{"resource.map", 0, "5cbeb95dd2a4b7cb242b415cc6ec1c47", 6444},
{"resource.001", 0, "a21451ef6fa8179bd4b22c4950004c44", 859959},
{"resource.002", 0, "a21451ef6fa8179bd4b22c4950004c44", 1136968},
{"resource.003", 0, "a21451ef6fa8179bd4b22c4950004c44", 769897},
- {NULL, 0, NULL, 0}},
- Common::JA_JPN, Common::kPlatformPC98, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Quest for Glory 1 - Japanese PC-98 5.25" Floppy
+ // Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes english language)
// Executable scanning reports "S.old.201"
{"qfg1", "16 Colors", {
{"resource.map", 0, "3ecaba33bf77cb434067a0b8aee15097", 6444},
{"resource.001", 0, "a21451ef6fa8179bd4b22c4950004c44", 864754},
{"resource.002", 0, "a21451ef6fa8179bd4b22c4950004c44", 1147121},
{"resource.003", 0, "a21451ef6fa8179bd4b22c4950004c44", 777575},
- {NULL, 0, NULL, 0}},
- Common::JA_JPN, Common::kPlatformPC98, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Quest for Glory 1 - English Amiga
// Executable scanning reports "1.002.020"
@@ -2204,9 +2371,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "16cd4414c37ae3bb6d6da33dce8e25e8", 654096},
{"resource.004", 0, "16cd4414c37ae3bb6d6da33dce8e25e8", 689124},
{"resource.005", 0, "5f3386ef2f2b1254e4a066f5d9027324", 609529},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Quest for Glory 1 (from abevi, bug report #2612718)
{"qfg1", "", {
@@ -2216,9 +2382,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635561},
{"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640502},
{"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644575},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Quest for Glory 1 - English DOS
// SCI interpreter version 0.000.629
@@ -2229,36 +2394,32 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "05ddce5f437a516b89ede2438fac09d8", 635734},
{"resource.003", 0, "951299a82a8134ed12c5c18118d45c2f", 640483},
{"resource.004", 0, "951299a82a8134ed12c5c18118d45c2f", 644443},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 1 VGA Remake - English DOS
// Executable scanning reports "2.000.411"
- {"qfg1", "VGA Remake", {
+ {"qfg1vga", "VGA Remake", {
{"resource.map", 0, "a731fb6c9c0b282443f7027bc8694d4c", 8469},
{"resource.000", 0, "ecace1a2771846b1a8aa1afdd44111a0", 6570147},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 1 VGA Remake - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.029
- {"qfg1", "VGA Remake, Demo", {
+ {"qfg1vga", "VGA Remake, Demo", {
{"resource.map", 0, "ac0257051c95a59c0cdc0be24d9b11fa", 729},
{"resource.000", 0, "ec6f5cf369054dd3e5392995e9975b9e", 768218},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Quest for Glory 1 VGA Remake - English Macintosh Floppy
// VERSION file reports "2.0"
- {"qfg1", "VGA Remake", {
+ {"qfg1vga", "VGA Remake", {
{"Data1", 0, "14f26bc75f24bb1ecc94532df17b5371", 1768155},
{"Data2", 0, "a7aee8bd46fc9cef7fd3bea93ef173e0", 6586422},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO_NOSPEECH },
// Quest for Glory 2 - English Amiga
// Executable scanning reports "1.003.004"
@@ -2273,9 +2434,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "a77d2576c842b2b06da57d4ac8fc51c0", 579975},
{"resource.006", 0, "ccf5dba33e5cab6d5872838c0f8db44c", 500039},
{"resource.007", 0, "4c9fc1587545879295cb9627f56a2cb8", 575056},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Quest for Glory 2 - English (from FRG)
// Executable scanning reports "1.000.072"
@@ -2286,9 +2446,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "df137dc7869cab07e1149ba2333c815c", 790750},
{"resource.003", 0, "b192607c42f6960ecdf2ad2e4f90e9bc", 972804},
{"resource.004", 0, "cd2de58e27665d5853530de93fae7cd6", 983617},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 2 - English DOS
// Executable scanning reports "1.000.072"
@@ -2302,54 +2461,48 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "df137dc7869cab07e1149ba2333c815c", 478688},
{"resource.006", 0, "b1944bd664ddbd2859cdaa0c4a0d6281", 507489},
{"resource.007", 0, "cd2de58e27665d5853530de93fae7cd6", 490794},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 2 - English DOS Non-Interactive Demo
// Executable scanning reports "1.000.046"
{"qfg2", "Demo", {
{"resource.map", 0, "e75eb86bdd517b3ef709058249986a87", 906},
{"resource.001", 0, "9b098f9e1008abe30e56c93b896494e6", 362123},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Quest for Glory 3 - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "1.001.021", VERSION file reports "1.000, 0.001.059, 6.12.92"
{"qfg3", "Demo", {
{"resource.map", 0, "fd71de9b588a45f085317caacf050e91", 687},
{"resource.000", 0, "b6c69bf6c18bf177492249fe81fc6a6d", 648702},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Quest for Glory 3 - English DOS
// SCI interpreter version 1.001.050
{"qfg3", "", {
{"resource.map", 0, "19e2bf9b693932b5e2bb59b9f9ab86c9", 5958},
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868000},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 3 - English DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 1.001.050
{"qfg3", "", {
{"resource.map", 0, "62c185d190363d7df06330fa0cc45b36", 5958},
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5867442},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 3 - German DOS (supplied by markcoolio in bug report #2723846)
// Executable scanning reports "L.rry.083"
{"qfg3", "", {
{"resource.map", 0, "19e2bf9b693932b5e2bb59b9f9ab86c9", 5958},
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868042},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 3 - Spanish DOS CD (from jvprat)
// Executable scanning reports "L.rry.083", VERSION file reports "1.000.000, June 30, 1994"
@@ -2357,18 +2510,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "10809197c33a5e62819311d8a2f73f85", 5978},
{"resource.000", 0, "ba7ac86155e4c531e46cd73c86daa80a", 5884098},
{"resource.msg", 0, "a63974730d294dec0bea10057c36e506", 256014},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
// Quest for Glory 4 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.069 (just a guess)
{"qfg4", "Demo", {
{"resource.map", 0, "1ba7c7ae1efb315326d45cb931569b1b", 922},
{"resource.000", 0, "41ba03f0b188b029132daa3ece0d3e14", 623154},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by markcool in bug report #2723852)
@@ -2376,58 +2527,33 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"qfg4", "", {
{"resource.map", 0, "685bdb1ed47bbbb0e5e25db392da83ce", 9301},
{"resource.000", 0, "f64fd6aa3977939a86ff30783dd677e1", 11004993},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 2.000.000
{"qfg4", "", {
{"resource.map", 0, "d10a4cc177d2091d744e2ad8c049b0ae", 9295},
{"resource.000", 0, "f64fd6aa3977939a86ff30783dd677e1", 11003589},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 4 1.1 Floppy - German DOS (supplied by markcool in bug report #2723850)
- // SCI interpreter version 2.000.000 (a guess?)
+ // Executable scanning reports "2.000.000", VERSION file reports "1.1"
{"qfg4", "", {
{"resource.map", 0, "9e0abba8746f40565bc7eb5720522ecd", 9301},
{"resource.000", 0, "57f22cdc54eeb35fce1f26b31b5c3ee1", 11076197},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Quest for Glory 4 - English DOS/Windows (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
{"qfg4", "", {
{"resource.map", 0, "aba367f2102e81782d961b14fbe3d630", 10246},
{"resource.000", 0, "263dce4aa34c49d3ad29bec889007b1c", 11571394},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
-
-#if 0
- // NOTE: This version looks to be exactly the same as the English one
- // Perhaps it's the English one?
-
- // Quest for Glory 4 - German DOS/Windows (from PCJoker 2/98)
- {"qfg4", "", {
- {"resource.map", 0, "aba367f2102e81782d961b14fbe3d630", 10246},
- {"resource.000", 0, "263dce4aa34c49d3ad29bec889007b1c", 11571394},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
-#endif
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Quest for Glory 4 - German DOS/Windows Disk V1.1 (from PCJoker 2/89)
- // SCI interpreter version 2.000.000 (a guess?)
- {"qfg4", "", {
- {"resource.map", 0, "9e0abba8746f40565bc7eb5720522ecd", 9301},
- {"resource.000", 0, "57f22cdc54eeb35fce1f26b31b5c3ee1", 11076197},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
#endif
// Slater & Charlie go camping
@@ -2435,9 +2561,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "1846b57fe84774be72f7c50ab3c90df0", 2256126},
{"resource.map", 0, "21f85414124dc23e54544a5536dc35cd", 4044},
{"resource.msg", 0, "c44f51fb955eae266fecf360ebcd5ad2", 1132},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
// RAMA - English DOS/Windows Demo
@@ -2445,9 +2570,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"rama", "Demo", {
{"resmap.001", 0, "775304e9b2a545156be4d94209550094", 1393},
{"ressci.001", 0, "259437fd75fdf51e8207fda8c01fa4fd", 2334384},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NONE },
#ifdef ENABLE_SCI3_GAMES
// RAMA - English Windows (from jvprat)
@@ -2459,9 +2583,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.002", 0, "2a68edd064e5e4937b5e9c74b38f2082", 128562138},
{"resmap.003", 0, "31ef4c0621711585d031f0ae81707251", 1636},
{"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6860492},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
// RAMA - English Windows (from Quietust, in bug report #2850645)
{"rama", "", {
@@ -2471,18 +2594,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.002", 0, "2a68edd064e5e4937b5e9c74b38f2082", 128572432},
{"resmap.003", 0, "48841e4b84ef1b98b48d43566fda9e13", 1636},
{"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6870356},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
// RAMA - Italian Windows CD (from glorifindel)
// SCI interpreter version 3.000.000 (a guess?)
{"rama", "", {
{"ressci.001", 0, "2a68edd064e5e4937b5e9c74b38f2082", 70611091},
{"resmap.001", 0, "70ba2ff04a2b7fb2c52420ba7fbd47c2", 8338},
- {NULL, 0, NULL, 0}},
- Common::IT_ITA, Common::kPlatformWindows, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::IT_ITA, Common::kPlatformWindows, 0, GUIO_NONE },
#endif // ENABLE_SCI3_GAMES
// Shivers - English Windows (from jvprat)
@@ -2490,26 +2611,23 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"shivers", "", {
{"resmap.000", 0, "f2ead37749ed8f6535a2445a7d05a0cc", 46525},
{"ressci.000", 0, "4294c6d7510935f2e0a52e302073c951", 262654836},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
// Shivers - German Windows (from Tobis87)
{"shivers", "", {
{"resmap.000", 0, "f483d0a1f78334c18052e92785c3086e", 46537},
{"ressci.000", 0, "6751b144671e2deed919eb9d284b07eb", 262390692},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformWindows, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformWindows, 0, GUIO_NONE },
// Shivers - English Windows Demo
// Executable scanning reports "2.100.002"
{"shivers", "Demo", {
{"resmap.000", 0, "d9e0bc5eddefcbe47f528760085d8927", 1186},
{"ressci.000", 0, "3a93c6340b54e07e65d0e5583354d186", 10505469},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NONE },
#ifdef ENABLE_SCI3_GAMES
@@ -2518,16 +2636,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"shivers2", "Demo", {
{"resmap.000", 0, "d8659188b84beaef076bd869837cd530", 634},
{"ressci.000", 0, "7fbac0807a044c9543e8ac376d200e59", 4925003},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NONE },
// Shivers 2 - English Windows (from abevi)
// VERSION.TXT Version 1.0 (3/25/97)
{"shivers2", "", {
{"ressci.001", 0, "a79d03d6eb75be0a79324f14e3d2ace4", 95346793},
{"resmap.001", 0, "a4804d436d90c4ec2e46b537f5e954db", 6268},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
#endif //ENABLE_SCI3_GAMES
@@ -2540,9 +2657,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "61b4f74039399e5aa1e737b16d0fc023", 1409},
{"resource.msg", 0, "1aeafe2b495de288d002109650b66614", 1364},
{"resource.000", 0, "8e10d4f05c1fd9f883384fa38a898489", 377394},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Space Quest 1 VGA Remake - English Amiga (from www.back2roots.org)
// SCI interpreter version 1.000.510 (just a guess)
@@ -2554,9 +2670,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "2588c1c2ca8b9bed0e3411948c0856a9", 839302},
{"resource.004", 0, "b25a1539c71701f7715f738c5037e9a6", 775515},
{"resource.005", 0, "640ffe1a9acde392cc33cc1b1a528328", 806324},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
// Space Quest 1 VGA Remake - English DOS (from the Space Quest Collection)
// Executable scanning reports "T.A00.081", VERSION file reports "2.000"
@@ -2568,18 +2683,27 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "a9e847c687529481f3a22b9bf01f45f7", 1169831},
{"resource.003", 0, "c47600e50c6fc591957ae0c5020ee7b8", 1213262},
{"resource.004", 0, "e19ea4ad131472f9238590f2e1d40289", 1203051},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Space Quest 1 VGA Remake - English Mac (from Fingolfin)
+ {"sq1sci", "VGA Remake", {
+ {"resource.map", 0, "5c6ad20407261b544238e8dce87afead", 5895},
+ {"resource.000", 0, "2c414644b23839069c8d1a93b721df16", 1017033},
+ {"resource.001", 0, "8744ae2ea6b316e91e2a35ab1aa301d2", 1024622},
+ {"resource.002", 0, "96860704f7a07ecc10bef223b4b2f153", 1273992},
+ {"resource.003", 0, "ae46e195e66df5a131917f0aa80b5669", 1242794},
+ {"resource.004", 0, "91d58a9eb2187c38424990afe4c12bc6", 1250949},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
// Space Quest 1 VGA Remake - English Non-Interactive Demo (from FRG)
// SCI interpreter version 1.000.181
{"sq1sci", "VGA Remake, Demo", {
{"resource.map", 0, "5af709ac5e0e923e0b8174f49978c30e", 636},
{"resource.001", 0, "fd99ea43f57576ded7c86036996346cf", 507642},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Space Quest 1 VGA Remake - Spanish DOS Floppy (from jvprat)
// Executable scanning reports "T.A00.081", VERSION file reports "2.000"
@@ -2592,9 +2716,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "8c22700a02991b763f512f837636b3ca", 1211307},
{"resource.004", 0, "9b78228ad4f9f335fedf74f1812dcfca", 513325},
{"resource.005", 0, "7d4ebcb745c0bf8fc42e4013f52ecd49", 1101812},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 3 - English Amiga (from www.back2roots.org)
// SCI interpreter version 0.000.453 (just a guess)
@@ -2604,10 +2727,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 754432},
{"resource.003", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 746496},
{"resource.004", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 761984},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
- // Space Quest 3 - German Amiga
+ // Space Quest 3 - German Amiga (also includes english language)
// Executable scanning reports "1.004.006"
// SCI interpreter version 0.000.453 (just a guess)
{"sq3", "", {
@@ -2617,15 +2740,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "6d8f34090503ce937e7dbef6cb6cdb6a", 712374},
{"resource.004", 0, "6d8f34090503ce937e7dbef6cb6cdb6a", 545053},
{"resource.005", 0, "6d8f34090503ce937e7dbef6cb6cdb6a", 687507},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Space Quest 3 - English DOS Non-Interactive Demo
// SCI interpreter version 0.000.453
{"sq3", "Demo", {
{"resource.map", 0, "ec66ac2b1ce58b2575ba00b65058de1a", 612},
{"resource.001", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 180245},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Space Quest 3 - English DOS (provided by richiefs in bug report #2670691)
@@ -2635,7 +2758,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 485158},
{"resource.002", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 720244},
{"resource.003", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 688367},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 3 - English DOS (from the Space Quest Collection)
@@ -2645,7 +2768,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 490247},
{"resource.002", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 715777},
{"resource.003", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 703370},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 3 - English DOS (from abevi, bug report #2612718)
@@ -2657,10 +2780,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 321222},
{"resource.005", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 328278},
{"resource.006", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 356702},
- {NULL, 0, NULL, 0}},
+ AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Space Quest 3 - German DOS (from Tobis87)
+ // Space Quest 3 - English Mac (from Fingolfin)
+ {"sq3", "", {
+ {"resource.map", 0, "5c931675c6e01c4b418faca85d76c92c", 5844},
+ {"resource.001", 0, "0d8dfe42683b46f3131823233a91ce6a", 771917},
+ {"resource.002", 0, "0d8dfe42683b46f3131823233a91ce6a", 794072},
+ {"resource.003", 0, "0d8dfe42683b46f3131823233a91ce6a", 776536},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
+
+ // Space Quest 3 - German DOS (from Tobis87, also includes english language)
// SCI interpreter version 0.000.453 (?)
{"sq3", "", {
{"resource.map", 0, "4965c78b5eff50d5e4148ce114594ba8", 7584},
@@ -2671,18 +2803,18 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "9107c2aa5398e28b5c5406df13491f85", 322107},
{"resource.006", 0, "9107c2aa5398e28b5c5406df13491f85", 320643},
{"resource.007", 0, "9107c2aa5398e28b5c5406df13491f85", 344287},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Space Quest 3 v1.052 - German DOS (supplied by markcoolio in bug report #2723860)
+ // Space Quest 3 v1.052 - German DOS (supplied by markcoolio in bug report #2723860, also includes english language)
// Executable scanning reports "S.old.114"
{"sq3", "", {
{"resource.map", 0, "f0dd735098c254f584878649c6f08dbc", 5154},
{"resource.001", 0, "9107c2aa5398e28b5c5406df13491f85", 567245},
{"resource.002", 0, "9107c2aa5398e28b5c5406df13491f85", 596768},
{"resource.003", 0, "9107c2aa5398e28b5c5406df13491f85", 693573},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Space Quest 4 - English Amiga
// Executable scanning reports "1.004.024"
@@ -2696,11 +2828,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "1887ed88bb34ae7238650e8f77f26315", 798226},
{"resource.005", 0, "3540d1cc84d674cf4b2c898b88a3b563", 790296},
{"resource.006", 0, "ade814bc4d56244c156d9e9bcfebbc11", 664085},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
- // Space Quest 4 - German Amiga (from www.back2roots.org)
+ // Space Quest 4 - German Amiga (from www.back2roots.org, also includes english language)
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
{"resource.map", 0, "79641c0d43408e33c251a1d494d2575e", 6252},
@@ -2711,19 +2842,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "99c6a017da5e769a3b427ca52c8a564f", 824601},
{"resource.005", 0, "10ee1709e6559c724676d058199b75b5", 818745},
{"resource.006", 0, "67fb188b191d88efe8414af6ea297b93", 672675},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformAmiga, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Space Quest 4 - English DOS
+#if 0
+ // Space Quest 4 - English DOS - THIS VERSION IS PIRATED/CRACKED AND REPACKAGED =DO NOT RE-ADD=
// Executable scanning reports "1.000.753"
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
{"resource.map", 0, "a18088c8aceb06025dbc945f29e02935", 5124},
{"resource.000", 0, "e1f46832cd2458796028e054a0466031", 5502009},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+#endif
// Space Quest 4 - English DOS
// Executable scanning reports "1.000.753"
@@ -2731,9 +2862,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"sq4", "", {
{"resource.map", 0, "71ccf4f82ac4efb588731acfb7bf2603", 5646},
{"resource.000", 0, "e1f46832cd2458796028e054a0466031", 933928},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 4 1.052 - English DOS Floppy (supplied by markcoolio in bug report #2723865)
// Executable scanning reports "1.000.753"
@@ -2746,9 +2876,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "96fa33d89d838bc3f671c5b953e7a896", 1240130},
{"resource.004", 0, "ff9c87da3bc53473fdee8b9d3edbc93c", 1200631},
{"resource.005", 0, "e33019ac19f755ae33fbf49b4fc9066c", 1053294},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 4 1.000 - English DOS Floppy (from abevi, bug report #2612718)
{"sq4", "", {
@@ -2759,11 +2888,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "47ee647b5b12232d27e63cc627c25899", 1321146},
{"resource.004", 0, "c06350184a490c10eb4585fff0aa3192", 1254368},
{"resource.005", 0, "b8d6efbd3235329bfe844c794097b2c9", 1098717},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Space Quest 4 - German DOS (from Tobis87)
+ // Space Quest 4 - German DOS (from Tobis87, also includes english language)
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
{"resource.map", 0, "71715e775e3791178d606cfe6c7e1fb9", 6339},
@@ -2774,11 +2902,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "b8d6efbd3235329bfe844c794097b2c9", 1064761},
{"resource.005", 0, "47ee647b5b12232d27e63cc627c25899", 1156765},
{"resource.006", 0, "dfb023e4e2a1e7a00fa18f9ede72a91b", 924059},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Space Quest 4 - Italian DOS Floppy (from glorifindel)
+ // Space Quest 4 - Italian DOS Floppy (from glorifindel, also includes english language)
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
{"resource.map", 0, "e753dfa96d68dd95f84f6cd80479a35e", 6135},
@@ -2788,42 +2915,38 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "5289000399d503b59da9e23129256f1a", 1325546},
{"resource.004", 0, "4277c61bed40a50dadc4b5a344520af2", 1251000},
{"resource.005", 0, "5f885abd335978e2fd4e5f886d7676c8", 1102880},
- {NULL, 0, NULL, 0}},
- Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Space Quest 4 - Japanese PC-98 5.25" Floppy
+ // Space Quest 4 - Japanese PC-98 5.25" Floppy (also includes english language)
// SCI interpreter version 1.000.1068
{"sq4", "", {
{"resource.map", 0, "ca7bba01019222b6f3e54e9051067a99", 5283},
{"resource.000", 0, "161d719f38ed98d33f058a8cf3dc09c3", 952909},
{"resource.001", 0, "454684e3a7a68cbca073945e50778447", 1187088},
{"resource.002", 0, "6dc668326cc22cb9e8bd8ca9e68d2a66", 1181249},
- {NULL, 0, NULL, 0}},
- Common::JA_JPN, Common::kPlatformPC98, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Space Quest 4 - Japanese PC-98 5.25" Floppy
+ // Space Quest 4 - Japanese PC-98 5.25" Floppy (also includes english language)
// SCI interpreter version 1.000.1068
{"sq4", "", {
{"resource.map", 0, "ca7bba01019222b6f3e54e9051067a99", 5283},
{"resource.000", 0, "161d719f38ed98d33f058a8cf3dc09c3", 952909},
{"resource.001", 0, "454684e3a7a68cbca073945e50778447", 1187088},
{"resource.002", 0, "6dc668326cc22cb9e8bd8ca9e68d2a66", 1181249},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC98, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Space Quest 4 - English DOS CD (from the Space Quest Collection)
// Executable scanning reports "1.001.064", VERSION file reports "1.0"
{"sq4", "CD", {
{"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054},
{"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
- // Space Quest 4 - Spanish DOS CD (from jvprat)
+ // Space Quest 4 - Spanish DOS CD (from jvprat, is still text only, not talkie, also includes english language)
// Executable scanning reports "1.SQ4.057", VERSION file reports "1.000"
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
@@ -2834,11 +2957,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "42a307941edeb1a3be31daeb2e4be90b", 1319306},
{"resource.004", 0, "776fba81c110d1908776232cbe190e20", 1253752},
{"resource.005", 0, "55fae26c2a92f16ef72c1e216e827c0f", 1098328},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NONE },
- // Space Quest 4 - Spanish DOS Floppy (from jvprat)
+ // Space Quest 4 - Spanish DOS Floppy (from jvprat, also includes english language)
// Executable scanning reports "1.SQ4.056", VERSION file reports "1.000"
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
@@ -2847,11 +2969,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "567608beb69d9dffdb42a8f39cb11a5e", 994323},
{"resource.002", 0, "74c62fa2146ff3b3b2ea2b3fb95b9af9", 1140801},
{"resource.003", 0, "42a307941edeb1a3be31daeb2e4be90b", 1088408},
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
- // Space Quest 4 1.000 - German DOS Floppy (supplied by markcoolio in bug report #2723862)
+ // Space Quest 4 1.000 - German DOS Floppy (supplied by markcoolio in bug report #2723862, also includes english language)
// Executable scanning reports "1.SQ4.030"
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
@@ -2862,9 +2983,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "47ee647b5b12232d27e63cc627c25899", 1321146},
{"resource.004", 0, "c06350184a490c10eb4585fff0aa3192", 1254368},
{"resource.005", 0, "b8d6efbd3235329bfe844c794097b2c9", 1098717},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
// Space Quest 4 - English Macintosh
// Executable scanning reports "x.yyy.zzz"
@@ -2878,9 +2998,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "20fc54556ebfc737506288a1a32f7705", 364217},
{"resource.005", 0, "869d16cab6641c80b06f4dcee18f86bc", 1426228},
{"resource.006", 0, "91d23407bc0447a3722fbeb952d7edee", 1402451},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
// Space Quest 5 - English DOS (from the Space Quest Collection)
// Executable scanning reports "1.001.068", VERSION file reports "1.04"
@@ -2888,18 +3007,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "66317c12ac6e818d1f7c17e83c1d9819", 6143},
{"resource.000", 0, "4147edc5045e6d62998018b5614c58ec", 5496486},
{"resource.msg", 0, "bb8ad78793c26bdb3f77498b1d6515a9", 125988},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 5 - English DOS
// SCI interpreter version 1.001.067
{"sq5", "", {
{"resource.map", 0, "8bde0a9adb9a3e9aaa861826874c9834", 6473},
{"resource.000", 0, "f4a48705764544d7cc64a7bb22a610df", 6025184},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 5 v1.04 - German DOS (from Tobis87, updated information by markcool from bug reports #2723935 and #2724762)
// SCI interpreter version 1.001.068
@@ -2907,27 +3024,24 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "66317c12ac6e818d1f7c17e83c1d9819", 6143},
{"resource.000", 0, "4147edc5045e6d62998018b5614c58ec", 5496486},
{"resource.msg", 0, "7c71cfc36153cfe07b450423a51f7e68", 146282},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 5 v1.04 - French DOS (from Hkz, Included in Space Quest Collector's Edition, with chapters I-V)
{"sq5", "", {
{"resource.map", 0, "66317c12ac6e818d1f7c17e83c1d9819", 6143},
{"resource.000", 0, "4147edc5045e6d62998018b5614c58ec", 5496486},
{"resource.msg", 0, "877c42380320eb1db7dad83ccd261214", 140374},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Space Quest 5 - Italian DOS Floppy (from glorifindel)
// SCI interpreter version 1.001.068 (just a guess)
{"sq5", "", {
{"resource.000", 0, "5040026519f37199f3616fb1d4704dff", 6047170},
{"resource.map", 0, "5b09168baa2f6e2e22787429b2d72f54", 6492},
- {NULL, 0, NULL, 0}},
- Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
// Space Quest 6 - English DOS/Win3.11 CD (from the Space Quest Collection)
@@ -2935,45 +3049,40 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"sq6", "", {
{"resource.map", 0, "6dddfa3a8f3a3a513ec9dfdfae955005", 10528},
{"resource.000", 0, "c4259ab7355aead07773397b1052827d", 41150806},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Space Quest 6 - English DOS/Win3.11 CD ver 1.11 (from FRG)
// SCI interpreter version 2.100.002 (just a guess)
{"sq6", "", {
{"resource.map", 0, "e0615d6e4e10e37ae42e6a2a95aaf145", 10528},
{"resource.000", 0, "c4259ab7355aead07773397b1052827d", 41150806},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// Space Quest 6 - French DOS/Win3.11 CD (from French magazine Joystick - September 1997)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
{"sq6", "", {
{"resource.map", 0, "3c831625931d5079b73ae8c275f52c95", 10534},
{"resource.000", 0, "4195ca940f759424f62b90e262cc1737", 40932397},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE },
// Space Quest 6 - German DOS (from Tobis87, updated info from markcoolio in bug report #2723884)
// SCI interpreter version 2.100.002 (just a guess)
{"sq6", "", {
{"resource.map", 0, "664d797415484f85c90b1b45aedc7686", 10534},
{"resource.000", 0, "ba87ba91e5bdabb4169dd0df75777722", 40933685},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE },
// Space Quest 6 - English DOS/Win3.11 Interactive Demo (from FRG)
// SCI interpreter version 2.100.002 (just a guess)
{"sq6", "Demo", {
{"resource.map", 0, "368f07b07433db3f819fa3fa0e5efee5", 2572},
{"resource.000", 0, "ab12724e078dea34b624e0d2a38dcd7c", 2272050},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
#endif // ENABLE_SCI32
// The Island of Dr. Brain - English DOS CD (from jvprat)
@@ -2981,27 +3090,24 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"islandbrain", "", {
{"resource.map", 0, "2388efef8430b041b0f3b00b9050e4a2", 3281},
{"resource.000", 0, "b3acd9b9dd7fe53c4ee133ac9a1acfab", 2103560},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
// The Island of Dr. Brain - English DOS (from Quietust)
// Executable scanning reports "1.001.053", VERSION file reports "1.1 2.3.93"
{"islandbrain", "", {
{"resource.map", 0, "3c07da06bdd1689f9d07af78fb94d0ec", 3101},
{"resource.000", 0, "ecc686e0034fb4d41de077ac7167b3cf", 1947866},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// The Island of Dr. Brain - English DOS Non-Interactive Demo
// SCI interpreter version 1.001.053 (just a guess)
{"islandbrain", "Demo", {
{"resource.map", 0, "a8e5ca8ed1996974afa59f4c45e06195", 986},
{"resource.000", 0, "b3acd9b9dd7fe53c4ee133ac9a1acfab", 586560},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
// Torin's Passage - English Windows Interactive Demo
@@ -3009,18 +3115,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"torin", "Demo", {
{"resmap.000", 0, "9a3e172cde9963d0a969f26469318cec", 3403},
{"ressci.000", 0, "db3e290481c35c3224e9602e71e4a1f1", 5073868},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NOSPEECH },
// Torin's Passage - English Windows
// SCI interpreter version 2.100.002 (just a guess)
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
- {NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// Torin's Passage - Spanish Windows (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -3028,59 +3132,66 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
// TODO: depend on one of the patches?
- {NULL, 0, NULL, 0}},
- Common::ES_ESP, Common::kPlatformWindows, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// Torin's Passage - French Windows
// SCI interpreter version 2.100.002 (just a guess)
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
- {NULL, 0, NULL, 0}},
- Common::FR_FRA, Common::kPlatformWindows, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// Torin's Passage - German Windows
// SCI interpreter version 2.100.002 (just a guess)
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformWindows, 0, GUIO_NOSPEECH
- },
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
// Torin's Passage - Italian Windows CD (from glorifindel)
// SCI interpreter version 2.100.002 (just a guess)
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
- {NULL, 0, NULL, 0}},
- Common::IT_ITA, Common::kPlatformWindows, 0, GUIO_NONE
- },
+ AD_LISTEND},
+ Common::IT_ITA, Common::kPlatformWindows, 0, GUIO_NONE },
#endif // ENABLE_SCI32
// SCI Fanmade Games
- FANMADE("Al Pond 2: Island Quest", "9625372e710d1a95d2027b48f9e325af", 1506, "a0f9aa65b9bf3d8703adff5a621f243c", 889843),
FANMADE("Al Pond: Island Quest 2", "4cba6a5a4c8f66f21935ed78b0511a92", 870, "876587dc9a5ec569287a3dc4b29139d8", 613769),
+ FANMADE("Al Pond 2: Island Quest", "9625372e710d1a95d2027b48f9e325af", 1506, "a0f9aa65b9bf3d8703adff5a621f243c", 889843),
+ FANMADE("Al Pond 2: Island Quest (Updated)", "64be277cdcc6aafce7d9f26e88ad31a8", 1500, "571547228a212d63315f0c114cf48d54", 885241),
FANMADE("Another DG Game: I Want My C64 Back", "4a8ca7ca2abd18899ef856f47665e2e9", 588, "12ff558d20c72e42cc6adb408f34d6d8", 150513),
FANMADE_L("Another DG Game: I Want My C64 Back", "13dc1d9ebc57daf8895412eee5e39fea", 576, "e2ad60b3a280171429db5c85f158f84a", 141697, Common::FR_FRA),
+ FANMADE("Aquarius: An Aquatic Experience", "2e23bc3b82f22a454be202ea593fb478", 480, "01555c8de683d25405bda270aa1ff014", 272372),
FANMADE("Bluntman and Chronic (Politically Correct Version)", "c3ef9fa6c7c5fb840078bf28d87c7f8b", 1362, "441636a9f6f86710844868fded868ee7", 596688),
FANMADE("Cascade Quest", "c94efc10d18c040b6e22a1dc6d3adfe1", 3468, "8ada33dfa945f81531e5508240b573de", 1432195),
- FANMADE("Curt Quest 1.0", "b0e555370380d218968a40a68eaaaffc", 1146, "c851182cdf6fc6a81b840f4d4875f1a0", 307165),
- FANMADE("Curt Quest 1.1", "54084c29346683296e45ef32d7ae74f3", 1128, "c851182cdf6fc6a81b840f4d4875f1a0", 302000),
+ FANMADE("Circus Quest", "35871f6b4e1df56af4113c0203a0b223", 630, "7d6f97d7935d8733f488d4cb74315e5b", 279627),
+ FANMADE("Curt's Quest 1.0", "b0e555370380d218968a40a68eaaaffc", 1146, "c851182cdf6fc6a81b840f4d4875f1a0", 307165),
+ FANMADE("Curt's Quest 1.1", "54084c29346683296e45ef32d7ae74f3", 1128, "c851182cdf6fc6a81b840f4d4875f1a0", 302000),
FANMADE("Demo Quest", "c89a0c9e0a4e4af0ecedb300a3b31dbf", 384, "a32f3495ba24764cba091119cc3f1e13", 160098),
FANMADE("Dr. Jummybummy's Space Adventure 2", "6ae6cb7de423f51736d9487b4ca0c6da", 810, "26e5b563f578e104d79689f36568b7cf", 394670),
FANMADE_L("Grostesteing: Plus Mechant que Jamais", "ec9a97ccb134f69249f6ea8b16c13d8e", 1500, "b869f5f11bfe2ab5f67f4f0c618f2ce1", 464657, Common::FR_FRA), // FIXME: Accent
- FANMADE("Jim Quest", "0af50be1d3f0cb77a09137709a76ef4f", 960, "9c042c136548b20d9183495668e03526", 496446),
+ FANMADE("Humanoid Demo", "97d8331293a6d57e8bad58c1efc89a63", 624, "fb354b9abe64011b12159e45d724633f", 452320),
+ FANMADE("Jim’s Quest 1: The Phantom Thesis", "0af50be1d3f0cb77a09137709a76ef4f", 960, "9c042c136548b20d9183495668e03526", 496446),
FANMADE("Knight's Quest Demo 1.0", "5e816edf993956752ed06fccfeeae6d9", 1260, "959321f88a22905fa1f8c6d897874744", 703836),
FANMADE("LockerGnome Quest", "3eeff9130206cad0c4e1551e2b9dd2c5", 420, "ae05ca90806fd90cc43f147c82d3547c", 158906),
- FANMADE("New Year's Mystery", "efd1beb5120293725065c95959144f81", 714, "b3bd3c2372ed6efa28adb12403c4c31a", 305027),
+ FANMADE("New Year's Mystery", "e4dcab1b1d3cb4a2c070a07a9c9589e0", 708, "e00ca5e44fd4e98d8174b467b31b0f21", 295425),
+ FANMADE("New Year's Mystery (Updated)", "efd1beb5120293725065c95959144f81", 714, "b3bd3c2372ed6efa28adb12403c4c31a", 305027),
+ FANMADE("Ocean Battle", "c2304a0568e0eb84f8e9a0915f01170a", 408, "46c520c1ac9b63528854d0f58c7e1b74", 142234),
FANMADE("Osama", "db8f1710453cfbecf4214b2946970043", 390, "7afd75d4620dedf97a84ae8f0a7523cf", 123827),
FANMADE("Quest for the Cheat", "a359d4cf27f98264b42b55c017671214", 882, "8a943029f73c4bc85d454b7f473455ba", 455209),
+ FANMADE("SCI Capture the Flag", "4cd679a51d93b8b27c6b38d81be24b8b", 432, "98ae1f6ed7b4c21f88addbf643dd1d2f", 147878),
FANMADE("SCI Companion Template", "ad54d4f504086cd597aa2348d0aa3b09", 354, "6798b7b601ce8154c1d1bc0f0edcdd18", 113061),
+ FANMADE("SCI Programming April 2010 Competition Template", "36e5c4011dd7c92e1ae4c6fede7d698d", 456, "20c87fbb7f73e2a3eb2c5dfab4d76b5a", 142221),
FANMADE("SCI Studio Template 3.0", "ca0dc8d586e0a8670b7621cde090b532", 354, "58a48ee692a86c0575e6bd0b00a92b9a", 113097),
FANMADE("SCI Quest", "9067e1f1e54436d2dbfce855524bc84a", 552, "ffa7d355cd9223f245289108a696bcd2", 149634),
+ FANMADE("SCI-Man", "3ab85bd39a86c11f85781764f9db09bb", 468, "bb8f9992f504a242bf0860e3588e150b", 131810),
+ FANMADE("The Farm Nightmare", "fb6cbfddaa7c055e2c3d8cf4c683a7db", 906, "50655e8b8925f717e698e08f006f40be", 338303),
+ FANMADE("The Gem Scenario", "ef5f61f4d2c6d31122d3e2baf89ad976", 642, "2f16be390dd90c3d7ca1c8a594ac0bfa", 244794),
FANMADE("The Legend of the Lost Jewel", "ba1bca315e3818c5626eda51bcfbcccf", 636, "9b0736d69924af0cff32a0f78db96855", 300398),
// FIXME: The vga demo does not have a resource.000/001 file.
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index e51867332a..f99d412c64 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -24,6 +24,7 @@
*/
#include "sci/engine/features.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/script.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -37,6 +38,7 @@ GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan)
_doSoundType = SCI_VERSION_NONE;
_lofsType = SCI_VERSION_NONE;
_gfxFunctionsType = SCI_VERSION_NONE;
+ _messageFunctionType = SCI_VERSION_NONE;
_moveCountType = kMoveCountUninitialized;
#ifdef ENABLE_SCI32
@@ -51,13 +53,13 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc
reg_t addr;
if (objAddr.isNull()) {
- warning("getDetectionAddr: %s object couldn't be found", objName.c_str());
+ error("getDetectionAddr: %s object couldn't be found", objName.c_str());
return NULL_REG;
}
if (methodNum == -1) {
- if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) {
- warning("getDetectionAddr: target selector is not a method of object %s", objName.c_str());
+ if (lookupSelector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) {
+ error("getDetectionAddr: target selector is not a method of object %s", objName.c_str());
return NULL_REG;
}
} else {
@@ -69,7 +71,7 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc
bool GameFeatures::autoDetectSoundType() {
// Look up the script address
- reg_t addr = getDetectionAddr("Sound", _kernel->_selectorCache.play);
+ reg_t addr = getDetectionAddr("Sound", SELECTOR(play));
if (!addr.segment)
return false;
@@ -83,17 +85,17 @@ bool GameFeatures::autoDetectSoundType() {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
- // The play method of the Sound object pushes the DoSound command
- // that it'll use just before it calls DoSound. We intercept that here
- // in order to check what sound semantics are used, cause the position
- // of the sound commands has changed at some point during SCI1 middle
+ // The play method of the Sound object pushes the DoSound command that
+ // it will use just before it calls DoSound. We intercept that here in
+ // order to check what sound semantics are used, cause the position of
+ // the sound commands has changed at some point during SCI1 middle.
if (opcode == op_pushi) {
// Load the pushi parameter
intParam = opparams[0];
@@ -104,8 +106,8 @@ bool GameFeatures::autoDetectSoundType() {
if (kFuncNum == 6) { // kIsObject (SCI0-SCI11)
foundTarget = true;
} else if (kFuncNum == 45) { // kDoSound (SCI1)
- // First, check which DoSound function is called by the play method of
- // the Sound object
+ // First, check which DoSound function is called by the play
+ // method of the Sound object
switch (intParam) {
case 1:
_doSoundType = SCI_VERSION_0_EARLY;
@@ -118,8 +120,8 @@ bool GameFeatures::autoDetectSoundType() {
break;
default:
// Unknown case... should never happen. We fall back to
- // alternative detection here, which works in general, apart from
- // some transitive games like Jones CD
+ // alternative detection here, which works in general, apart
+ // from some transitive games like Jones CD
_doSoundType = foundTarget ? SCI_VERSION_1_LATE : SCI_VERSION_1_EARLY;
break;
}
@@ -136,9 +138,14 @@ bool GameFeatures::autoDetectSoundType() {
SciVersion GameFeatures::detectDoSoundType() {
if (_doSoundType == SCI_VERSION_NONE) {
if (getSciVersion() == SCI_VERSION_0_EARLY) {
- // This game is using early SCI0 sound code (different headers than SCI0 late)
+ // This game is using early SCI0 sound code (different headers than
+ // SCI0 late)
_doSoundType = SCI_VERSION_0_EARLY;
- } else if (_kernel->_selectorCache.nodePtr == -1) {
+#ifdef ENABLE_SCI32
+ } else if (getSciVersion() >= SCI_VERSION_2_1) {
+ _doSoundType = SCI_VERSION_2_1;
+#endif
+ } else if (SELECTOR(nodePtr) == -1) {
// No nodePtr selector, so this game is definitely using newer
// SCI0 sound code (i.e. SCI_VERSION_0_LATE)
_doSoundType = SCI_VERSION_0_LATE;
@@ -171,14 +178,16 @@ SciVersion GameFeatures::detectSetCursorType() {
// SCI1.1 games always use cursor views
_setCursorType = SCI_VERSION_1_1;
} else { // SCI1 late game, detect cursor semantics
- // If the Cursor object doesn't exist, we're using the SCI0 early kSetCursor semantics.
+ // If the Cursor object doesn't exist, we're using the SCI0 early
+ // kSetCursor semantics.
if (_segMan->findObjectByName("Cursor") == NULL_REG) {
_setCursorType = SCI_VERSION_0_EARLY;
debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType));
return _setCursorType;
}
- // Check for the existence of the handCursor object (first found). This is based on KQ5.
+ // Check for the existence of the handCursor object (first found).
+ // This is based on KQ5.
reg_t objAddr = _segMan->findObjectByName("handCursor", 0);
// If that doesn't exist, we assume it uses SCI1.1 kSetCursor semantics
@@ -188,11 +197,13 @@ SciVersion GameFeatures::detectSetCursorType() {
return _setCursorType;
}
- // Now we check what the number variable holds in the handCursor object.
- uint16 number = GET_SEL32V(_segMan, objAddr, SELECTOR(number));
+ // Now we check what the number variable holds in the handCursor
+ // object.
+ uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number));
- // If the number is 0, it uses views and therefore the SCI1.1 kSetCursor semantics,
- // otherwise it uses the SCI0 early kSetCursor semantics.
+ // If the number is 0, it uses views and therefore the SCI1.1
+ // kSetCursor semantics, otherwise it uses the SCI0 early kSetCursor
+ // semantics.
if (number == 0)
_setCursorType = SCI_VERSION_1_1;
else
@@ -205,9 +216,9 @@ SciVersion GameFeatures::detectSetCursorType() {
return _setCursorType;
}
-bool GameFeatures::autoDetectLofsType(int methodNum) {
+bool GameFeatures::autoDetectLofsType(Common::String gameSuperClassName, int methodNum) {
// Look up the script address
- reg_t addr = getDetectionAddr("Game", -1, methodNum);
+ reg_t addr = getDetectionAddr(gameSuperClassName.c_str(), -1, methodNum);
if (!addr.segment)
return false;
@@ -219,11 +230,11 @@ bool GameFeatures::autoDetectLofsType(int methodNum) {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_lofsa || opcode == op_lofss) {
@@ -231,13 +242,13 @@ bool GameFeatures::autoDetectLofsType(int methodNum) {
uint16 lofs = opparams[0];
// Check for going out of bounds when interpreting as abs/rel
- if (lofs >= script->_bufSize)
+ if (lofs >= script->getBufSize())
_lofsType = SCI_VERSION_0_EARLY;
if ((signed)offset + (int16)lofs < 0)
_lofsType = SCI_VERSION_1_MIDDLE;
- if ((signed)offset + (int16)lofs >= (signed)script->_bufSize)
+ if ((signed)offset + (int16)lofs >= (signed)script->getBufSize())
_lofsType = SCI_VERSION_1_MIDDLE;
if (_lofsType != SCI_VERSION_NONE)
@@ -264,20 +275,35 @@ SciVersion GameFeatures::detectLofsType() {
return _lofsType;
}
+ // Find the "Game" object, super class of the actual game-object
+ const reg_t game = g_sci->getGameObject();
+ const Object *gameObject = _segMan->getObject(game);
+ reg_t gameSuperClass = NULL_REG;
+ if (gameObject) {
+ gameSuperClass = gameObject->getSuperClassSelector();
+ }
+
// Find a function of the game object which invokes lofsa/lofss
- reg_t gameClass = _segMan->findObjectByName("Game");
- Object *obj = _segMan->getObject(gameClass);
bool found = false;
+ if (!gameSuperClass.isNull()) {
+ Common::String gameSuperClassName = _segMan->getObjectName(gameSuperClass);
+ const Object *gameSuperObject = _segMan->getObject(gameSuperClass);
- for (uint m = 0; m < obj->getMethodCount(); m++) {
- found = autoDetectLofsType(m);
-
- if (found)
- break;
+ if (gameSuperObject) {
+ for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) {
+ found = autoDetectLofsType(gameSuperClassName, m);
+ if (found)
+ break;
+ }
+ } else {
+ warning("detectLofsType(): Could not get superclass object");
+ }
+ } else {
+ warning("detectLofsType(): Could not find superclass of game object");
}
if (!found) {
- warning("Lofs detection failed, taking an educated guess");
+ warning("detectLofsType(): failed, taking an educated guess");
if (getSciVersion() >= SCI_VERSION_1_MIDDLE)
_lofsType = SCI_VERSION_1_MIDDLE;
@@ -293,7 +319,7 @@ SciVersion GameFeatures::detectLofsType() {
bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
// Look up the script address
- reg_t addr = getDetectionAddr("Rm", _kernel->_selectorCache.overlay, methodNum);
+ reg_t addr = getDetectionAddr("Rm", SELECTOR(overlay), methodNum);
if (!addr.segment)
return false;
@@ -305,11 +331,11 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_callk) {
@@ -317,10 +343,10 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
uint16 argc = opparams[1];
if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11)
- // If kDrawPic is called with 6 parameters from the
- // overlay selector, the game is using old graphics functions.
+ // If kDrawPic is called with 6 parameters from the overlay
+ // selector, the game is using old graphics functions.
// Otherwise, if it's called with 8 parameters, it's using new
- // graphics functions
+ // graphics functions.
_gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY;
return true;
}
@@ -332,18 +358,50 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
SciVersion GameFeatures::detectGfxFunctionsType() {
if (_gfxFunctionsType == SCI_VERSION_NONE) {
- // This detection only works (and is only needed) for SCI0 games
- if (getSciVersion() >= SCI_VERSION_01) {
+ if (getSciVersion() == SCI_VERSION_0_EARLY) {
+ // Old SCI0 games always used old graphics functions
+ _gfxFunctionsType = SCI_VERSION_0_EARLY;
+ } else if (getSciVersion() >= SCI_VERSION_01) {
+ // SCI01 and newer games always used new graphics functions
_gfxFunctionsType = SCI_VERSION_0_LATE;
- } else if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ } else { // SCI0 late
// Check if the game is using an overlay
- bool found = false;
+ bool searchRoomObj = false;
+ reg_t rmObjAddr = _segMan->findObjectByName("Rm");
+
+ if (SELECTOR(overlay) != -1) {
+ // The game has an overlay selector, check how it calls kDrawPic
+ // to determine the graphics functions type used
+ if (lookupSelector(_segMan, rmObjAddr, SELECTOR(overlay), NULL, NULL) == kSelectorMethod) {
+ if (!autoDetectGfxFunctionsType()) {
+ warning("Graphics functions detection failed, taking an educated guess");
+
+ // Try detecting the graphics function types from the
+ // existence of the motionCue selector (which is a bit
+ // of a hack)
+ if (_kernel->findSelector("motionCue") != -1)
+ _gfxFunctionsType = SCI_VERSION_0_LATE;
+ else
+ _gfxFunctionsType = SCI_VERSION_0_EARLY;
+ }
+ } else {
+ // The game has an overlay selector, but it's not a method
+ // of the Rm object (like in Hoyle 1 and 2), so search for
+ // other methods
+ searchRoomObj = true;
+ }
+ } else {
+ // The game doesn't have an overlay selector, so search for it
+ // manually
+ searchRoomObj = true;
+ }
- if (_kernel->_selectorCache.overlay == -1) {
- // No overlay selector found, check if any method of the Rm object
- // is calling kDrawPic, as the overlay selector might be missing in demos
+ if (searchRoomObj) {
+ // If requested, check if any method of the Rm object is calling
+ // kDrawPic, as the overlay selector might be missing in demos
+ bool found = false;
- Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm"));
+ const Object *obj = _segMan->getObject(rmObjAddr);
for (uint m = 0; m < obj->getMethodCount(); m++) {
found = autoDetectGfxFunctionsType(m);
if (found)
@@ -351,30 +409,12 @@ SciVersion GameFeatures::detectGfxFunctionsType() {
}
if (!found) {
- // No overlay selector found, therefore the game is definitely
- // using old graphics functions
+ // No method of the Rm object is calling kDrawPic, thus the
+ // game doesn't have overlays and is using older graphics
+ // functions
_gfxFunctionsType = SCI_VERSION_0_EARLY;
}
- } else { // _kernel->_selectorCache.overlay != -1
- // An in-between case: The game does not have a shiftParser
- // selector, but it does have an overlay selector, so it uses an
- // overlay. Therefore, check it to see how it calls kDrawPic to
- // determine the graphics functions type used
-
- if (!autoDetectGfxFunctionsType()) {
- warning("Graphics functions detection failed, taking an educated guess");
-
- // Try detecting the graphics function types from the existence of the motionCue
- // selector (which is a bit of a hack)
- if (_kernel->findSelector("motionCue") != -1)
- _gfxFunctionsType = SCI_VERSION_0_LATE;
- else
- _gfxFunctionsType = SCI_VERSION_0_EARLY;
- }
}
- } else { // (getSciVersion() == SCI_VERSION_0_EARLY)
- // Old SCI0 games always used old graphics functions
- _gfxFunctionsType = SCI_VERSION_0_EARLY;
}
debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType));
@@ -383,10 +423,60 @@ SciVersion GameFeatures::detectGfxFunctionsType() {
return _gfxFunctionsType;
}
+SciVersion GameFeatures::detectMessageFunctionType() {
+ if (_messageFunctionType != SCI_VERSION_NONE)
+ return _messageFunctionType;
+
+ if (getSciVersion() > SCI_VERSION_1_1) {
+ _messageFunctionType = SCI_VERSION_1_1;
+ return _messageFunctionType;
+ } else if (getSciVersion() < SCI_VERSION_1_1) {
+ _messageFunctionType = SCI_VERSION_1_LATE;
+ return _messageFunctionType;
+ }
+
+ Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1);
+
+ if (resources->empty()) {
+ delete resources;
+
+ // No messages found, so this doesn't really matter anyway...
+ _messageFunctionType = SCI_VERSION_1_1;
+ return _messageFunctionType;
+ }
+
+ Resource *res = g_sci->getResMan()->findResource(*resources->begin(), false);
+ assert(res);
+ delete resources;
+
+ // Only v2 Message resources use the kGetMessage kernel function.
+ // v3-v5 use the kMessage kernel function.
+
+ if (READ_SCI11ENDIAN_UINT32(res->data) / 1000 == 2)
+ _messageFunctionType = SCI_VERSION_1_LATE;
+ else
+ _messageFunctionType = SCI_VERSION_1_1;
+
+ debugC(1, kDebugLevelVM, "Detected message function type: %s", getSciVersionDesc(_messageFunctionType));
+ return _messageFunctionType;
+}
+
#ifdef ENABLE_SCI32
bool GameFeatures::autoDetectSci21KernelType() {
+ // First, check if the Sound object is loaded
+ reg_t soundObjAddr = _segMan->findObjectByName("Sound");
+ if (soundObjAddr.isNull()) {
+ // Usually, this means that the Sound object isn't loaded yet.
+ // This case doesn't occur in early SCI2.1 games, and we've only
+ // seen it happen in the RAMA demo, thus we can assume that the
+ // game is using a SCI2.1 table
+ warning("autoDetectSci21KernelType(): Sound object not loaded, assuming a SCI2.1 table");
+ _sci21KernelType = SCI_VERSION_2_1;
+ return true;
+ }
+
// Look up the script address
- reg_t addr = getDetectionAddr("Sound", _kernel->_selectorCache.play);
+ reg_t addr = getDetectionAddr("Sound", SELECTOR(play));
if (!addr.segment)
return false;
@@ -398,20 +488,22 @@ bool GameFeatures::autoDetectSci21KernelType() {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_callk) {
uint16 kFuncNum = opparams[0];
// Here we check for the kDoSound opcode that's used in SCI2.1.
- // Finding 0x40 as kDoSound in the Sound::play() function means the game is using
- // the modified SCI2 kernel table found in some older SCI2.1 games (GK2 demo, KQ7 v1.4).
- // Finding 0x75 as kDoSound means the game is using the regular SCI2.1 kernel table.
+ // Finding 0x40 as kDoSound in the Sound::play() function means the
+ // game is using the modified SCI2 kernel table found in some older
+ // SCI2.1 games (GK2 demo, KQ7 v1.4).
+ // Finding 0x75 as kDoSound means the game is using the regular
+ // SCI2.1 kernel table.
if (kFuncNum == 0x40) {
_sci21KernelType = SCI_VERSION_2;
return true;
@@ -438,7 +530,7 @@ SciVersion GameFeatures::detectSci21KernelType() {
bool GameFeatures::autoDetectMoveCountType() {
// Look up the script address
- reg_t addr = getDetectionAddr("Motion", _kernel->_selectorCache.doit);
+ reg_t addr = getDetectionAddr("Motion", SELECTOR(doit));
if (!addr.segment)
return false;
@@ -451,11 +543,11 @@ bool GameFeatures::autoDetectMoveCountType() {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_callk) {
@@ -481,7 +573,7 @@ MoveCountType GameFeatures::detectMoveCountType() {
_moveCountType = kIncrementMoveCount;
} else {
if (!autoDetectMoveCountType()) {
- warning("Move count autodetection failed");
+ error("Move count autodetection failed");
_moveCountType = kIncrementMoveCount; // Most games do this, so best guess
}
}
diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h
index 77c2f0cff7..755054fb25 100644
--- a/engines/sci/engine/features.h
+++ b/engines/sci/engine/features.h
@@ -31,6 +31,12 @@
namespace Sci {
+enum MoveCountType {
+ kMoveCountUninitialized,
+ kIgnoreMoveCount,
+ kIncrementMoveCount
+};
+
class GameFeatures {
public:
GameFeatures(SegManager *segMan, Kernel *kernel);
@@ -60,6 +66,12 @@ public:
* @return Graphics functions type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE
*/
SciVersion detectGfxFunctionsType();
+
+ /**
+ * Autodetects the message function used
+ * @return Message function type, SCI_VERSION_1_LATE / SCI_VERSION_1_1
+ */
+ SciVersion detectMessageFunctionType();
#ifdef ENABLE_SCI32
/**
@@ -91,7 +103,7 @@ public:
private:
reg_t getDetectionAddr(const Common::String &objName, Selector slc, int methodNum = -1);
- bool autoDetectLofsType(int methodNum);
+ bool autoDetectLofsType(Common::String gameSuperClassName, int methodNum);
bool autoDetectGfxFunctionsType(int methodNum = -1);
bool autoDetectSoundType();
bool autoDetectMoveCountType();
@@ -99,7 +111,7 @@ private:
bool autoDetectSci21KernelType();
#endif
- SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType;
+ SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType, _messageFunctionType;
#ifdef ENABLE_SCI32
SciVersion _sci21KernelType;
#endif
diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp
deleted file mode 100644
index 4ac2a22531..0000000000
--- a/engines/sci/engine/game.cpp
+++ /dev/null
@@ -1,320 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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/system.h"
-#include "common/file.h"
-
-#include "engines/advancedDetector.h" // for ADGF_DEMO
-
-#include "sci/sci.h"
-#include "sci/resource.h"
-#include "sci/engine/features.h"
-#include "sci/engine/state.h"
-#include "sci/engine/kernel.h"
-#include "sci/engine/message.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/menu.h"
-#include "sci/sound/audio.h"
-#include "sci/sound/music.h"
-
-namespace Sci {
-
-struct OldNewIdTableEntry {
- const char *oldId;
- const char *newId;
- SciVersion version;
-};
-
-static const OldNewIdTableEntry s_oldNewTable[] = {
- { "arthur", "camelot", SCI_VERSION_NONE },
- { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga
- { "brain", "castlebrain", SCI_VERSION_1_LATE },
- { "demo", "christmas1988", SCI_VERSION_NONE },
- { "card", "christmas1990", SCI_VERSION_1_EARLY, },
- { "card", "christmas1992", SCI_VERSION_1_1 },
- { "RH Budget", "cnick-longbow", SCI_VERSION_NONE },
- // iceman is the same
- { "icedemo", "iceman", SCI_VERSION_NONE },
- // longbow is the same
- { "eco", "ecoquest", SCI_VERSION_NONE },
- { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo
- { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full
- { "fp", "freddypharkas", SCI_VERSION_NONE },
- { "emc", "funseeker", SCI_VERSION_NONE },
- { "gk", "gk1", SCI_VERSION_NONE },
- { "hoyledemo", "hoyle1", SCI_VERSION_NONE },
- { "cardgames", "hoyle1", SCI_VERSION_NONE },
- { "solitare", "hoyle2", SCI_VERSION_NONE },
- // hoyle3 is the same
- // hoyle4 is the same
- { "brain", "islandbrain", SCI_VERSION_1_1 },
- { "demo000", "kq1sci", SCI_VERSION_NONE },
- { "kq1", "kq1sci", SCI_VERSION_NONE },
- { "kq4", "kq4sci", SCI_VERSION_NONE },
- { "mm1", "laurabow", SCI_VERSION_NONE },
- { "cb1", "laurabow", SCI_VERSION_NONE },
- { "lb2", "laurabow2", SCI_VERSION_NONE },
- { "rh", "longbow", SCI_VERSION_NONE },
- { "ll1", "lsl1sci", SCI_VERSION_NONE },
- { "lsl1", "lsl1sci", SCI_VERSION_NONE },
- // lsl2 is the same
- { "lsl3", "lsl3", SCI_VERSION_NONE },
- { "ll5", "lsl5", SCI_VERSION_NONE },
- // lsl5 is the same
- // lsl6 is the same
- { "mg", "mothergoose", SCI_VERSION_NONE },
- { "twisty", "pepper", SCI_VERSION_NONE },
- { "pq1", "pq1sci", SCI_VERSION_NONE },
- { "pq", "pq2", SCI_VERSION_NONE },
- // pq3 is the same
- // pq4 is the same
- { "tales", "fairytales", SCI_VERSION_NONE },
- { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA
- { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA
- { "trial", "qfg2", SCI_VERSION_NONE },
- { "hq2demo", "qfg2", SCI_VERSION_NONE },
- { "thegame", "slater", SCI_VERSION_NONE },
- { "sq1demo", "sq1sci", SCI_VERSION_NONE },
- { "sq1", "sq1sci", SCI_VERSION_NONE },
- // sq3 is the same
- // sq4 is the same
- // sq5 is the same
- // torin is the same
-
- // TODO: SCI2.1, SCI3 IDs
-
- { "", "", SCI_VERSION_NONE }
-};
-
-Common::String convertSierraGameId(const char *gameId, uint32 *gameFlags, ResourceManager *resMan) {
- // Convert the id to lower case, so that we match all upper/lower case variants.
- Common::String sierraId = gameId;
- sierraId.toLowercase();
-
- // If the game has less than the expected scripts, it's a demo
- uint32 demoThreshold = 100;
- // ...but there are some exceptions
- if (sierraId == "brain" || sierraId == "lsl1" ||
- sierraId == "mg" || sierraId == "pq" ||
- sierraId == "jones" ||
- sierraId == "cardgames" || sierraId == "solitare" ||
- sierraId == "hoyle3" || sierraId == "hoyle4")
- demoThreshold = 40;
- if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4")
- demoThreshold = 150;
-
- Common::List<ResourceId> *resources = resMan->listResources(kResourceTypeScript, -1);
- if (resources->size() < demoThreshold) {
- *gameFlags |= ADGF_DEMO;
-
- // Crazy Nick's Picks
- if (sierraId == "lsl1" && resources->size() == 34)
- return "cnick-lsl";
- if (sierraId == "sq4" && resources->size() == 34)
- return "cnick-sq";
-
- // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read)
-
- // Handle Astrochicken 1 (SQ3) and 2 (SQ4)
- if (sierraId == "sq3" && resources->size() == 20)
- return "astrochicken";
- if (sierraId == "sq4")
- return "msastrochicken";
- }
-
- for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) {
- if (sierraId == cur->oldId) {
- // Distinguish same IDs from the SCI version
- if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion())
- continue;
-
- return cur->newId;
- }
- }
-
- if (sierraId == "glory") {
- // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1),
- // or qfg4 full (SCI2)
- // qfg1 VGA doesn't have view 1
- if (!resMan->testResource(ResourceId(kResourceTypeView, 1)))
- return "qfg1";
-
- // qfg4 full is SCI2
- if (getSciVersion() == SCI_VERSION_2)
- return "qfg4";
-
- // qfg4 demo has less than 50 scripts
- if (resources->size() < 50)
- return "qfg4";
-
- // Otherwise it's qfg3
- return "qfg3";
- }
-
- return sierraId;
-}
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-int game_init_sound(EngineState *s, int sound_flags, SciVersion soundVersion) {
- if (getSciVersion() > SCI_VERSION_0_LATE)
- sound_flags |= SFX_STATE_FLAG_MULTIPLAY;
-
- s->sfx_init_flags = sound_flags;
- s->_sound.sfx_init(g_sci->getResMan(), sound_flags, soundVersion);
-
- return 0;
-}
-#endif
-
-// Architectural stuff: Init/Unintialize engine
-int script_init_engine(EngineState *s) {
- s->_msgState = new MessageState(s->_segMan);
- s->gc_countdown = GC_INTERVAL - 1;
-
- SegmentId script_000_segment = s->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK);
-
- if (script_000_segment <= 0) {
- debug(2, "Failed to instantiate script.000");
- return 1;
- }
-
- s->script_000 = s->_segMan->getScript(script_000_segment);
-
- s->sys_strings = s->_segMan->allocateSysStrings(&s->sys_strings_segment);
-
- // Allocate static buffer for savegame and CWD directories
- SystemString *str = &s->sys_strings->_strings[SYS_STRING_SAVEDIR];
- str->_name = "savedir";
- str->_maxSize = MAX_SAVE_DIR_SIZE;
- str->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char));
-
- s->r_acc = s->r_prev = NULL_REG;
- s->restAdjust = 0;
-
- s->_executionStack.clear(); // Start without any execution stack
- s->execution_stack_base = -1; // No vm is running yet
-
- s->restarting_flags = SCI_GAME_IS_NOT_RESTARTING;
-
- debug(2, "Engine initialized");
-
- return 0;
-}
-
-/*************************************************************/
-/* Game instance stuff: Init/Unitialize state-dependant data */
-/*************************************************************/
-
-int game_init(EngineState *s) {
- // FIXME Use new VM instantiation code all over the place
- DataStack *stack;
-
- stack = s->_segMan->allocateStack(VM_STACK_SIZE, NULL);
- s->stack_base = stack->_entries;
- s->stack_top = stack->_entries + stack->_capacity;
-
- if (!script_instantiate(g_sci->getResMan(), s->_segMan, 0)) {
- warning("game_init(): Could not instantiate script 0");
- return 1;
- }
-
- if (s->_voc) {
- s->_voc->parserIsValid = false; // Invalidate parser
- s->_voc->parser_event = NULL_REG; // Invalidate parser event
- s->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE);
- }
-
- // Initialize menu TODO: Actually this should be another init()
- if (g_sci->_gfxMenu)
- g_sci->_gfxMenu->reset();
-
- s->successor = NULL; // No successor
-
- SystemString *str = &s->sys_strings->_strings[SYS_STRING_PARSER_BASE];
- str->_name = "parser-base";
- str->_maxSize = MAX_PARSER_BASE;
- str->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char));
-
- s->game_start_time = g_system->getMillis();
- s->last_wait_time = s->game_start_time;
-
- srand(g_system->getMillis()); // Initialize random number generator
-
-// script_dissect(0, s->_selectorNames);
- // The first entry in the export table of script 0 points to the game object
- s->_gameObj = s->_segMan->lookupScriptExport(0, 0);
- uint32 gameFlags = 0; // unused
- s->_gameId = convertSierraGameId(s->_segMan->getObjectName(s->_gameObj), &gameFlags, g_sci->getResMan());
-
- debug(2, " \"%s\" at %04x:%04x", s->_gameId.c_str(), PRINT_REG(s->_gameObj));
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND)
- game_init_sound(s, 0, g_sci->_features->detectDoSoundType());
-#endif
-
- // Load game language into printLang property of game object
- // FIXME: It's evil to achieve this as a side effect of a getter.
- // Much better to have an explicit init method for this.
- g_sci->getSciLanguage();
-
- return 0;
-}
-
-int game_exit(EngineState *s) {
- s->_executionStack.clear();
-
- if (!s->successor) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- s->_sound.sfx_exit();
- // Reinit because some other code depends on having a valid state
- game_init_sound(s, SFX_STATE_FLAG_NOSOUND, g_sci->_features->detectDoSoundType());
-#else
- g_sci->_audio->stopAllAudio();
- s->_soundCmd->clearPlayList();
-#endif
- }
-
- // Note: It's a bad idea to delete the segment manager here
- // when loading a game.
- // This function is called right after a game is loaded, and
- // the segment manager has already been initialized from the
- // save game. Deleting or resetting it here will result in
- // invalidating the loaded save state
- if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW)
- s->_segMan->resetSegMan();
-
- // TODO Free parser segment here
-
- // TODO Free scripts here
-
- // Close all opened file handles
- s->_fileHandles.clear();
- s->_fileHandles.resize(5);
-
- return 0;
-}
-
-} // End of namespace Sci
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp
index c2f1c15776..936b83d760 100644
--- a/engines/sci/engine/gc.cpp
+++ b/engines/sci/engine/gc.cpp
@@ -28,11 +28,9 @@
namespace Sci {
-//#define DEBUG_GC
-
struct WorklistManager {
Common::Array<reg_t> _worklist;
- reg_t_hash_map _map;
+ AddrSet _map;
void push(reg_t reg) {
if (!reg.segment) // No numbers
@@ -46,14 +44,19 @@ struct WorklistManager {
_map.setVal(reg, true);
_worklist.push_back(reg);
}
+
+ void pushArray(const Common::Array<reg_t> &tmp) {
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+ push(*it);
+ }
};
-static reg_t_hash_map *normalise_hashmap_ptrs(SegManager *segMan, reg_t_hash_map &nonnormal_map) {
- reg_t_hash_map *normal_map = new reg_t_hash_map();
+static AddrSet *normalizeAddresses(SegManager *segMan, const AddrSet &nonnormal_map) {
+ AddrSet *normal_map = new AddrSet();
- for (reg_t_hash_map::iterator i = nonnormal_map.begin(); i != nonnormal_map.end(); ++i) {
+ for (AddrSet::const_iterator i = nonnormal_map.begin(); i != nonnormal_map.end(); ++i) {
reg_t reg = i->_key;
- SegmentObj *mobj = (reg.segment < segMan->_heap.size()) ? segMan->_heap[reg.segment] : NULL;
+ SegmentObj *mobj = segMan->getSegmentObj(reg.segment);
if (mobj) {
reg = mobj->findCanonicAddress(segMan, reg);
@@ -65,14 +68,9 @@ static reg_t_hash_map *normalise_hashmap_ptrs(SegManager *segMan, reg_t_hash_map
}
-void add_outgoing_refs(void *refcon, reg_t addr) {
- WorklistManager *wm = (WorklistManager *)refcon;
- wm->push(addr);
-}
-
-reg_t_hash_map *find_all_used_references(EngineState *s) {
+AddrSet *findAllActiveReferences(EngineState *s) {
SegManager *segMan = s->_segMan;
- reg_t_hash_map *normal_map = NULL;
+ AddrSet *normal_map = NULL;
WorklistManager wm;
uint i;
@@ -84,22 +82,19 @@ reg_t_hash_map *find_all_used_references(EngineState *s) {
wm.push(s->r_prev);
// Init: Value Stack
// We do this one by hand since the stack doesn't know the current execution stack
- Common::List<ExecStack>::iterator iter;
- {
- iter = s->_executionStack.reverse_begin();
+ Common::List<ExecStack>::iterator iter = s->_executionStack.reverse_begin();
- // Skip fake kernel stack frame if it's on top
- if (((*iter).type == EXEC_STACK_TYPE_KERNEL))
- --iter;
+ // Skip fake kernel stack frame if it's on top
+ if (((*iter).type == EXEC_STACK_TYPE_KERNEL))
+ --iter;
- assert((iter != s->_executionStack.end()) && ((*iter).type != EXEC_STACK_TYPE_KERNEL));
+ assert((iter != s->_executionStack.end()) && ((*iter).type != EXEC_STACK_TYPE_KERNEL));
- ExecStack &xs = *iter;
- reg_t *pos;
+ ExecStack &xs = *iter;
+ reg_t *pos;
- for (pos = s->stack_base; pos < xs.sp; pos++)
- wm.push(*pos);
- }
+ for (pos = s->stack_base; pos < xs.sp; pos++)
+ wm.push(*pos);
debugC(2, kDebugLevelGC, "[GC] -- Finished adding value stack");
@@ -118,24 +113,18 @@ reg_t_hash_map *find_all_used_references(EngineState *s) {
debugC(2, kDebugLevelGC, "[GC] -- Finished adding execution stack");
+ const Common::Array<SegmentObj *> &heap = segMan->getSegments();
+
// Init: Explicitly loaded scripts
- for (i = 1; i < segMan->_heap.size(); i++)
- if (segMan->_heap[i]
- && segMan->_heap[i]->getType() == SEG_TYPE_SCRIPT) {
- Script *script = (Script *)segMan->_heap[i];
+ for (i = 1; i < heap.size(); i++) {
+ if (heap[i] && heap[i]->getType() == SEG_TYPE_SCRIPT) {
+ Script *script = (Script *)heap[i];
if (script->getLockers()) { // Explicitly loaded?
- // Locals, if present
- wm.push(make_reg(script->_localsSegment, 0));
-
- // All objects (may be classes, may be indirectly reachable)
- ObjMap::iterator it;
- const ObjMap::iterator end = script->_objects.end();
- for (it = script->_objects.begin(); it != end; ++it) {
- wm.push(it->_value.getPos());
- }
+ wm.pushArray(script->listObjectReferences());
}
}
+ }
debugC(2, kDebugLevelGC, "[GC] -- Finished explicitly loaded scripts, done with root set");
@@ -146,76 +135,64 @@ reg_t_hash_map *find_all_used_references(EngineState *s) {
wm._worklist.pop_back();
if (reg.segment != stack_seg) { // No need to repeat this one
debugC(2, kDebugLevelGC, "[GC] Checking %04x:%04x", PRINT_REG(reg));
- if (reg.segment < segMan->_heap.size() && segMan->_heap[reg.segment])
- segMan->_heap[reg.segment]->listAllOutgoingReferences(reg, &wm, add_outgoing_refs);
+ if (reg.segment < heap.size() && heap[reg.segment]) {
+ // Valid heap object? Find its outgoing references!
+ wm.pushArray(heap[reg.segment]->listAllOutgoingReferences(reg));
+ }
}
}
// Normalise
- normal_map = normalise_hashmap_ptrs(segMan, wm._map);
+ normal_map = normalizeAddresses(segMan, wm._map);
return normal_map;
}
-struct deallocator_t {
- SegManager *segMan;
- SegmentObj *mobj;
-#ifdef DEBUG_GC
- char *segnames[SEG_TYPE_MAX + 1];
- int segcount[SEG_TYPE_MAX + 1];
-#endif
- reg_t_hash_map *use_map;
-};
-
-void free_unless_used(void *refcon, reg_t addr) {
- deallocator_t *deallocator = (deallocator_t *)refcon;
- reg_t_hash_map *use_map = deallocator->use_map;
-
- if (!use_map->contains(addr)) {
- // Not found -> we can free it
- deallocator->mobj->freeAtAddress(deallocator->segMan, addr);
-#ifdef DEBUG_GC
- debugC(2, kDebugLevelGC, "[GC] Deallocating %04x:%04x", PRINT_REG(addr));
- deallocator->segcount[deallocator->mobj->getType()]++;
-#endif
- }
-
-}
-
void run_gc(EngineState *s) {
- uint seg_nr;
- deallocator_t deallocator;
SegManager *segMan = s->_segMan;
-#ifdef DEBUG_GC
+ // Some debug stuff
debugC(2, kDebugLevelGC, "[GC] Running...");
- memset(&(deallocator.segcount), 0, sizeof(int) * (SEG_TYPE_MAX + 1));
-#endif
-
- deallocator.segMan = segMan;
- deallocator.use_map = find_all_used_references(s);
-
- for (seg_nr = 1; seg_nr < segMan->_heap.size(); seg_nr++) {
- if (segMan->_heap[seg_nr] != NULL) {
- deallocator.mobj = segMan->_heap[seg_nr];
-#ifdef DEBUG_GC
- deallocator.segnames[deallocator.mobj->getType()] = deallocator.mobj->type; // FIXME: add a segment "name"
-#endif
- deallocator.mobj->listAllDeallocatable(seg_nr, &deallocator, free_unless_used);
+ const char *segnames[SEG_TYPE_MAX + 1];
+ int segcount[SEG_TYPE_MAX + 1];
+ memset(segnames, 0, sizeof(segnames));
+ memset(segcount, 0, sizeof(segcount));
+
+ // Compute the set of all segments references currently in use.
+ AddrSet *activeRefs = findAllActiveReferences(s);
+
+ // Iterate over all segments, and check for each whether it
+ // contains stuff that can be collected.
+ const Common::Array<SegmentObj *> &heap = segMan->getSegments();
+ for (uint seg = 1; seg < heap.size(); seg++) {
+ SegmentObj *mobj = heap[seg];
+ if (mobj != NULL) {
+ const SegmentType type = mobj->getType();
+ segnames[type] = SegmentObj::getSegmentTypeName(type);
+
+ // Get a list of all deallocatable objects in this segment,
+ // then free any which are not referenced from somewhere.
+ const Common::Array<reg_t> tmp = mobj->listAllDeallocatable(seg);
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it) {
+ const reg_t addr = *it;
+ if (!activeRefs->contains(addr)) {
+ // Not found -> we can free it
+ mobj->freeAtAddress(segMan, addr);
+ debugC(2, kDebugLevelGC, "[GC] Deallocating %04x:%04x", PRINT_REG(addr));
+ segcount[type]++;
+ }
+ }
+
}
}
- delete deallocator.use_map;
+ delete activeRefs;
-#ifdef DEBUG_GC
- {
- int i;
- debugC(2, kDebugLevelGC, "[GC] Summary:");
- for (i = 0; i <= SEG_TYPE_MAX; i++)
- if (deallocator.segcount[i])
- debugC(2, kDebugLevelGC, "\t%d\t* %s", deallocator.segcount[i], deallocator.segnames[i]);
- }
-#endif
+ // Output debug summary of garbage collection
+ debugC(2, kDebugLevelGC, "[GC] Summary:");
+ for (int i = 0; i <= SEG_TYPE_MAX; i++)
+ if (segcount[i])
+ debugC(2, kDebugLevelGC, "\t%d\t* %s", segcount[i], segnames[i]);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/gc.h b/engines/sci/engine/gc.h
index 9f9347ca18..f4318a1453 100644
--- a/engines/sci/engine/gc.h
+++ b/engines/sci/engine/gc.h
@@ -33,29 +33,24 @@
namespace Sci {
-struct reg_t_EqualTo {
- bool operator()(const reg_t& x, const reg_t& y) const {
- return (x.segment == y.segment) && (x.offset == y.offset);
- }
-};
-
struct reg_t_Hash {
uint operator()(const reg_t& x) const {
- return (x.segment << 3) | x.offset;
+ return (x.segment << 3) ^ x.offset ^ (x.offset << 16);
}
};
/*
- * The reg_t_hash_map is actually really a hashset
+ * The AddrSet is a "set" of reg_t values.
+ * We don't have a HashSet type, so we abuse a HashMap for this.
*/
-typedef Common::HashMap<reg_t, bool, reg_t_Hash, reg_t_EqualTo> reg_t_hash_map;
+typedef Common::HashMap<reg_t, bool, reg_t_Hash> AddrSet;
/**
* Finds all used references and normalises them to their memory addresses
* @param s The state to gather all information from
* @return A hash map containing entries for all used references
*/
-reg_t_hash_map *find_all_used_references(EngineState *s);
+AddrSet *findAllActiveReferences(EngineState *s);
/**
* Runs garbage collection on the current system state
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 49266f3a18..157884fac3 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -27,388 +27,52 @@
#include "sci/engine/kernel.h"
#include "sci/event.h"
#include "sci/resource.h"
+#include "sci/engine/features.h"
+#include "sci/engine/kernel_tables.h"
#include "sci/engine/state.h"
+#include "sci/engine/workarounds.h"
#include "common/system.h"
namespace Sci {
-// Default kernel name table
-#define SCI_KNAMES_DEFAULT_ENTRIES_NR 0x89
-
-static const char *sci_default_knames[SCI_KNAMES_DEFAULT_ENTRIES_NR] = {
- /*0x00*/ "Load",
- /*0x01*/ "UnLoad",
- /*0x02*/ "ScriptID",
- /*0x03*/ "DisposeScript",
- /*0x04*/ "Clone",
- /*0x05*/ "DisposeClone",
- /*0x06*/ "IsObject",
- /*0x07*/ "RespondsTo",
- /*0x08*/ "DrawPic",
- /*0x09*/ "Dummy", // Show
- /*0x0a*/ "PicNotValid",
- /*0x0b*/ "Animate",
- /*0x0c*/ "SetNowSeen",
- /*0x0d*/ "NumLoops",
- /*0x0e*/ "NumCels",
- /*0x0f*/ "CelWide",
- /*0x10*/ "CelHigh",
- /*0x11*/ "DrawCel",
- /*0x12*/ "AddToPic",
- /*0x13*/ "NewWindow",
- /*0x14*/ "GetPort",
- /*0x15*/ "SetPort",
- /*0x16*/ "DisposeWindow",
- /*0x17*/ "DrawControl",
- /*0x18*/ "HiliteControl",
- /*0x19*/ "EditControl",
- /*0x1a*/ "TextSize",
- /*0x1b*/ "Display",
- /*0x1c*/ "GetEvent",
- /*0x1d*/ "GlobalToLocal",
- /*0x1e*/ "LocalToGlobal",
- /*0x1f*/ "MapKeyToDir",
- /*0x20*/ "DrawMenuBar",
- /*0x21*/ "MenuSelect",
- /*0x22*/ "AddMenu",
- /*0x23*/ "DrawStatus",
- /*0x24*/ "Parse",
- /*0x25*/ "Said",
- /*0x26*/ "SetSynonyms", // Portrait (KQ6 hires)
- /*0x27*/ "HaveMouse",
- /*0x28*/ "SetCursor",
- // FOpen (SCI0)
- // FPuts (SCI0)
- // FGets (SCI0)
- // FClose (SCI0)
- /*0x29*/ "SaveGame",
- /*0x2a*/ "RestoreGame",
- /*0x2b*/ "RestartGame",
- /*0x2c*/ "GameIsRestarting",
- /*0x2d*/ "DoSound",
- /*0x2e*/ "NewList",
- /*0x2f*/ "DisposeList",
- /*0x30*/ "NewNode",
- /*0x31*/ "FirstNode",
- /*0x32*/ "LastNode",
- /*0x33*/ "EmptyList",
- /*0x34*/ "NextNode",
- /*0x35*/ "PrevNode",
- /*0x36*/ "NodeValue",
- /*0x37*/ "AddAfter",
- /*0x38*/ "AddToFront",
- /*0x39*/ "AddToEnd",
- /*0x3a*/ "FindKey",
- /*0x3b*/ "DeleteKey",
- /*0x3c*/ "Random",
- /*0x3d*/ "Abs",
- /*0x3e*/ "Sqrt",
- /*0x3f*/ "GetAngle",
- /*0x40*/ "GetDistance",
- /*0x41*/ "Wait",
- /*0x42*/ "GetTime",
- /*0x43*/ "StrEnd",
- /*0x44*/ "StrCat",
- /*0x45*/ "StrCmp",
- /*0x46*/ "StrLen",
- /*0x47*/ "StrCpy",
- /*0x48*/ "Format",
- /*0x49*/ "GetFarText",
- /*0x4a*/ "ReadNumber",
- /*0x4b*/ "BaseSetter",
- /*0x4c*/ "DirLoop",
- /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions
- /*0x4e*/ "OnControl",
- /*0x4f*/ "InitBresen",
- /*0x50*/ "DoBresen",
- /*0x51*/ "Platform", // DoAvoider (SCI0)
- /*0x52*/ "SetJump",
- /*0x53*/ "SetDebug",
- /*0x54*/ "Dummy", // InspectObj
- /*0x55*/ "Dummy", // ShowSends
- /*0x56*/ "Dummy", // ShowObjs
- /*0x57*/ "Dummy", // ShowFree
- /*0x58*/ "MemoryInfo",
- /*0x59*/ "Dummy", // StackUsage
- /*0x5a*/ "Dummy", // Profiler
- /*0x5b*/ "GetMenu",
- /*0x5c*/ "SetMenu",
- /*0x5d*/ "GetSaveFiles",
- /*0x5e*/ "GetCWD",
- /*0x5f*/ "CheckFreeSpace",
- /*0x60*/ "ValidPath",
- /*0x61*/ "CoordPri",
- /*0x62*/ "StrAt",
- /*0x63*/ "DeviceInfo",
- /*0x64*/ "GetSaveDir",
- /*0x65*/ "CheckSaveGame",
- /*0x66*/ "ShakeScreen",
- /*0x67*/ "FlushResources",
- /*0x68*/ "SinMult",
- /*0x69*/ "CosMult",
- /*0x6a*/ "SinDiv",
- /*0x6b*/ "CosDiv",
- /*0x6c*/ "Graph",
- /*0x6d*/ "Joystick",
- // End of kernel function table for SCI0
- /*0x6e*/ "ShiftScreen",
- /*0x6f*/ "Palette",
- /*0x70*/ "MemorySegment",
- /*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1)
- /*0x72*/ "Memory",
- /*0x73*/ "ListOps",
- /*0x74*/ "FileIO",
- /*0x75*/ "DoAudio",
- /*0x76*/ "DoSync",
- /*0x77*/ "AvoidPath",
- /*0x78*/ "Sort", // StrSplit (SCI01)
- /*0x79*/ "ATan",
- /*0x7a*/ "Lock",
- /*0x7b*/ "StrSplit",
- /*0x7c*/ "GetMessage", // Message (SCI1.1)
- /*0x7d*/ "IsItSkip",
- /*0x7e*/ "MergePoly",
- /*0x7f*/ "ResCheck",
- /*0x80*/ "AssertPalette",
- /*0x81*/ "TextColors",
- /*0x82*/ "TextFonts",
- /*0x83*/ "Dummy", // Record
- /*0x84*/ "Dummy", // PlayBack
- /*0x85*/ "ShowMovie",
- /*0x86*/ "SetVideoMode",
- /*0x87*/ "SetQuitStr",
- /*0x88*/ "Dummy" // DbugStr
-};
-
-struct SciKernelFunction {
- const char *name;
- KernelFunc *fun; /* The actual function */
- const char *signature; /* kfunct signature */
-};
-
-#define DEFUN(name, fun, sig) {name, fun, sig}
-
-SciKernelFunction kfunct_mappers[] = {
- /*00*/ DEFUN("Load", kLoad, "iii*"),
- /*01*/ DEFUN("UnLoad", kUnLoad, "i.*"), // Work around SQ1 bug, when exiting the Ulence flats bar
- /*02*/ DEFUN("ScriptID", kScriptID, "Ioi*"),
- /*03*/ DEFUN("DisposeScript", kDisposeScript, "Oii*"), // Work around QfG1 bug
- /*04*/ DEFUN("Clone", kClone, "o"),
- /*05*/ DEFUN("DisposeClone", kDisposeClone, "o"),
- /*06*/ DEFUN("IsObject", kIsObject, "."),
- /*07*/ DEFUN("RespondsTo", kRespondsTo, ".i"),
- /*08*/ DEFUN("DrawPic", kDrawPic, "i*"),
-
- /*0a*/ DEFUN("PicNotValid", kPicNotValid, "i*"),
- /*0b*/ DEFUN("Animate", kAnimate, "LI*"), // More like (li?)?
- /*0c*/ DEFUN("SetNowSeen", kSetNowSeen, "oi*"), // The second parameter is ignored
- /*0d*/ DEFUN("NumLoops", kNumLoops, "o"),
- /*0e*/ DEFUN("NumCels", kNumCels, "o"),
- /*0f*/ DEFUN("CelWide", kCelWide, "iOi*"),
- /*10*/ DEFUN("CelHigh", kCelHigh, "iOi*"),
- /*11*/ DEFUN("DrawCel", kDrawCel, "iiiiii*i*r*"),
- /*12*/ DEFUN("AddToPic", kAddToPic, "Il*"),
- // FIXME: signature check removed (set to .*) as kNewWindow is different in Mac versions
- /*13*/ DEFUN("NewWindow", kNewWindow, "*."),
- ///*13*/ DEFUN("NewWindow", kNewWindow, "iiiiZRi*"),
- /*14*/ DEFUN("GetPort", kGetPort, ""),
- /*15*/ DEFUN("SetPort", kSetPort, "ii*"),
- /*16*/ DEFUN("DisposeWindow", kDisposeWindow, "ii*"),
- /*17*/ DEFUN("DrawControl", kDrawControl, "o"),
- /*18*/ DEFUN("HiliteControl", kHiliteControl, "o"),
- /*19*/ DEFUN("EditControl", kEditControl, "ZoZo"),
- /*1a*/ DEFUN("TextSize", kTextSize, "rZrii*r*"),
- /*1b*/ DEFUN("Display", kDisplay, ".*"),
- /*1c*/ DEFUN("GetEvent", kGetEvent, "ioi*"), // Mac versions pass an extra 3rd parameter (ignored - always 0?)
- /*1d*/ DEFUN("GlobalToLocal", kGlobalToLocal, "oo*"),
- /*1e*/ DEFUN("LocalToGlobal", kLocalToGlobal, "oo*"),
- /*1f*/ DEFUN("MapKeyToDir", kMapKeyToDir, "o"),
- /*20*/ DEFUN("DrawMenuBar", kDrawMenuBar, "i"),
- /*21*/ DEFUN("MenuSelect", kMenuSelect, "oi*"),
- /*22*/ DEFUN("AddMenu", kAddMenu, "rr"),
- /*23*/ DEFUN("DrawStatus", kDrawStatus, "Zri*"),
- /*24*/ DEFUN("Parse", kParse, "ro"),
- /*25*/ DEFUN("Said", kSaid, "Zr"),
- /*26*/ DEFUN("SetSynonyms", kSetSynonyms, "o"),
- /*27*/ DEFUN("HaveMouse", kHaveMouse, ""),
- /*28*/ DEFUN("SetCursor", kSetCursor, "i*"),
- // FIXME: The number 0x28 occurs twice :-)
- /*28*/ DEFUN("MoveCursor", kMoveCursor, "ii"),
- /*29*/ DEFUN("FOpen", kFOpen, "ri"),
- /*2a*/ DEFUN("FPuts", kFPuts, "ir"),
- /*2b*/ DEFUN("FGets", kFGets, "rii"),
- /*2c*/ DEFUN("FClose", kFClose, "i"),
- /*2d*/ DEFUN("SaveGame", kSaveGame, "rirr*"),
- /*2e*/ DEFUN("RestoreGame", kRestoreGame, "rir*"),
- /*2f*/ DEFUN("RestartGame", kRestartGame, ""),
- /*30*/ DEFUN("GameIsRestarting", kGameIsRestarting, "i*"),
- /*31*/ DEFUN("DoSound", kDoSound, "iIo*"),
- /*32*/ DEFUN("NewList", kNewList, ""),
- /*33*/ DEFUN("DisposeList", kDisposeList, "l"),
- /*34*/ DEFUN("NewNode", kNewNode, ".."),
- /*35*/ DEFUN("FirstNode", kFirstNode, "Zl"),
- /*36*/ DEFUN("LastNode", kLastNode, "l"),
- /*37*/ DEFUN("EmptyList", kEmptyList, "l"),
- /*38*/ DEFUN("NextNode", kNextNode, "n"),
- /*39*/ DEFUN("PrevNode", kPrevNode, "n"),
- /*3a*/ DEFUN("NodeValue", kNodeValue, "Zn"),
- /*3b*/ DEFUN("AddAfter", kAddAfter, "lnn"),
- /*3c*/ DEFUN("AddToFront", kAddToFront, "ln"),
- /*3d*/ DEFUN("AddToEnd", kAddToEnd, "ln"),
- /*3e*/ DEFUN("FindKey", kFindKey, "l."),
- /*3f*/ DEFUN("DeleteKey", kDeleteKey, "l."),
- /*40*/ DEFUN("Random", kRandom, "i*"),
- /*41*/ DEFUN("Abs", kAbs, "Oi"),
- /*42*/ DEFUN("Sqrt", kSqrt, "i"),
- /*43*/ DEFUN("GetAngle", kGetAngle, "iiiii*"), // occasionally KQ6 passes a 5th argument by mistake
- /*44*/ DEFUN("GetDistance", kGetDistance, "iiiii*"),
- /*45*/ DEFUN("Wait", kWait, "i"),
- /*46*/ DEFUN("GetTime", kGetTime, "i*"),
- /*47*/ DEFUN("StrEnd", kStrEnd, "r"),
- /*48*/ DEFUN("StrCat", kStrCat, "rr"),
- /*49*/ DEFUN("StrCmp", kStrCmp, "rri*"),
- /*4a*/ DEFUN("StrLen", kStrLen, "Zr"),
- /*4b*/ DEFUN("StrCpy", kStrCpy, "rZri*"),
- /*4c*/ DEFUN("Format", kFormat, "r.*"),
- /*4d*/ DEFUN("GetFarText", kGetFarText, "iiZr"),
- /*4e*/ DEFUN("ReadNumber", kReadNumber, "r"),
- /*4f*/ DEFUN("BaseSetter", kBaseSetter, "o"),
- /*50*/ DEFUN("DirLoop", kDirLoop, "oi"),
- // Opcode 51 is defined twice for a reason: In older SCI versions
- // it is CanBeHere, whereas in newer version it is CantBeHere
- /*51*/ DEFUN("CanBeHere", kCanBeHere, "ol*"),
- /*51*/ DEFUN("CantBeHere", kCantBeHere, "ol*"),
- /*52*/ DEFUN("OnControl", kOnControl, "i*"),
- /*53*/ DEFUN("InitBresen", kInitBresen, "oi*"),
- /*54*/ DEFUN("DoBresen", kDoBresen, "o"),
- /*55*/ DEFUN("DoAvoider", kDoAvoider, "o"),
- /*56*/ DEFUN("SetJump", kSetJump, "oiii"),
- /*57*/ DEFUN("SetDebug", kSetDebug, "i*"),
- /*5c*/ DEFUN("MemoryInfo", kMemoryInfo, "i"),
- /*5f*/ DEFUN("GetMenu", kGetMenu, "i."),
- /*60*/ DEFUN("SetMenu", kSetMenu, "i.*"),
- /*61*/ DEFUN("GetSaveFiles", kGetSaveFiles, "rrr"),
- /*62*/ DEFUN("GetCWD", kGetCWD, "r"),
- /*63*/ DEFUN("CheckFreeSpace", kCheckFreeSpace, "r.*"),
- /*64*/ DEFUN("ValidPath", kValidPath, "r"),
- /*65*/ DEFUN("CoordPri", kCoordPri, "ii*"),
- /*66*/ DEFUN("StrAt", kStrAt, "rii*"),
- /*67*/ DEFUN("DeviceInfo", kDeviceInfo, "i.*"),
- /*68*/ DEFUN("GetSaveDir", kGetSaveDir, ".*"), // accepts a parameter in SCI2+ games
- /*69*/ DEFUN("CheckSaveGame", kCheckSaveGame, ".*"),
- /*6a*/ DEFUN("ShakeScreen", kShakeScreen, "ii*"),
- /*6b*/ DEFUN("FlushResources", kFlushResources, "i"),
- /*6c*/ DEFUN("TimesSin", kTimesSin, "ii"),
- /*6d*/ DEFUN("TimesCos", kTimesCos, "ii"),
- /*6e*/ DEFUN("6e", kTimesSin, "ii"),
- /*6f*/ DEFUN("6f", kTimesCos, "ii"),
- /*70*/ DEFUN("Graph", kGraph, ".*"),
- /*71*/ DEFUN("Joystick", kJoystick, ".*"),
-
- // Experimental functions
- /*74*/ DEFUN("FileIO", kFileIO, "i.*"),
- /*(?)*/ DEFUN("Memory", kMemory, "i.*"),
- /*(?)*/ DEFUN("Sort", kSort, "ooo"),
- /*(?)*/ DEFUN("AvoidPath", kAvoidPath, "ii.*"),
- /*(?)*/ DEFUN("Lock", kLock, "iii*"),
- /*(?)*/ DEFUN("Palette", kPalette, "i.*"),
- /*(?)*/ DEFUN("IsItSkip", kIsItSkip, "iiiii"),
- /*7b*/ DEFUN("StrSplit", kStrSplit, "rrZr"),
-
- // Non-experimental functions without a fixed ID
- DEFUN("CosMult", kTimesCos, "ii"),
- DEFUN("SinMult", kTimesSin, "ii"),
-
- // Misc functions
- /*(?)*/ DEFUN("CosDiv", kCosDiv, "ii"),
- /*(?)*/ DEFUN("PriCoord", kPriCoord, "i"),
- /*(?)*/ DEFUN("SinDiv", kSinDiv, "ii"),
- /*(?)*/ DEFUN("TimesCot", kTimesCot, "ii"),
- /*(?)*/ DEFUN("TimesTan", kTimesTan, "ii"),
- DEFUN("Message", kMessage, ".*"),
- DEFUN("GetMessage", kGetMessage, "iiir"),
- DEFUN("DoAudio", kDoAudio, ".*"),
- DEFUN("DoSync", kDoSync, ".*"),
- DEFUN("MemorySegment", kMemorySegment, "iri*"),
- DEFUN("Intersections", kIntersections, "iiiiriiiri"),
- DEFUN("ResCheck", kResCheck, "iii*"),
- DEFUN("SetQuitStr", kSetQuitStr, "r"),
- DEFUN("ShowMovie", kShowMovie, ".*"),
- DEFUN("SetVideoMode", kSetVideoMode, "i"),
- DEFUN("Platform", kPlatform, "i.*"),
- DEFUN("TextColors", kTextColors, ".*"),
- DEFUN("TextFonts", kTextFonts, ".*"),
- DEFUN("Portrait", kPortrait, ".*"),
-
-#ifdef ENABLE_SCI32
- // SCI2 Kernel Functions
- DEFUN("IsHiRes", kIsHiRes, ""),
- DEFUN("Array", kArray, ".*"),
- DEFUN("ListAt", kListAt, "li"),
- DEFUN("String", kString, ".*"),
- DEFUN("AddScreenItem", kAddScreenItem, "o"),
- DEFUN("UpdateScreenItem", kUpdateScreenItem, "o"),
- DEFUN("DeleteScreenItem", kDeleteScreenItem, "o"),
- DEFUN("AddPlane", kAddPlane, "o"),
- DEFUN("DeletePlane", kDeletePlane, "o"),
- DEFUN("UpdatePlane", kUpdatePlane, "o"),
- DEFUN("RepaintPlane", kRepaintPlane, "o"),
- DEFUN("GetHighPlanePri", kGetHighPlanePri, ""),
- DEFUN("FrameOut", kFrameOut, ""),
- DEFUN("ListEachElementDo", kListEachElementDo, "li.*"),
- DEFUN("ListFirstTrue", kListFirstTrue, "li.*"),
- DEFUN("ListAllTrue", kListAllTrue, "li.*"),
- DEFUN("ListIndexOf", kListIndexOf, "lZo"),
- DEFUN("OnMe", kOnMe, "iio.*"),
- DEFUN("InPolygon", kInPolygon, "iio"),
- DEFUN("CreateTextBitmap", kCreateTextBitmap, "iiio"),
-
- // SCI2.1 Kernel Functions
- DEFUN("Save", kSave, ".*"),
- DEFUN("List", kList, ".*"),
- DEFUN("Robot", kRobot, ".*"),
- DEFUN("IsOnMe", kOnMe, "iio.*"), // TODO: this seems right, but verify...
-
-#endif
-
- // its a stub, but its needed for Pharkas to work
- DEFUN("PalVary", kPalVary, "ii*"),
- DEFUN("AssertPalette", kAssertPalette, "i"),
-
-#if 0
- // Stub functions
- /*09*/ DEFUN("Show", kShow, "i"),
- DEFUN("ShiftScreen", kShiftScreen, ".*"),
- DEFUN("ListOps", kListOps, ".*"),
- DEFUN("ATan", kATan, ".*"),
- DEFUN("MergePoly", kMergePoly, ".*"),
- DEFUN("Record", kRecord, ".*"),
- DEFUN("PlayBack", kPlayBack, ".*"),
- DEFUN("DbugStr", kDbugStr, ".*"),
-#endif
-
- {NULL, NULL, NULL} // Terminator
-};
-
-Kernel::Kernel(ResourceManager *resMan, SegManager *segMan) : _resMan(resMan), _segMan(segMan) {
+Kernel::Kernel(ResourceManager *resMan, SegManager *segMan)
+ : _resMan(resMan), _segMan(segMan), _invalid("<invalid>") {
loadSelectorNames();
mapSelectors(); // Map a few special selectors for later use
}
Kernel::~Kernel() {
- for (KernelFuncsContainer::iterator i = _kernelFuncs.begin(); i != _kernelFuncs.end(); ++i)
- // TODO: Doing a const_cast is not that nice actually... But since KernelFuncWithSignature
- // keeps the signature member as "const char *" there is no way around it.
- // Think of a clever way to avoid this.
- free(const_cast<char *>(i->signature));
+ for (KernelFunctionArray::iterator it = _kernelFuncs.begin(); it != _kernelFuncs.end(); ++it) {
+ if (it->subFunctionCount) {
+ uint16 subFunctionNr = 0;
+ while (subFunctionNr < it->subFunctionCount) {
+ delete[] it->subFunctions[subFunctionNr].signature;
+ subFunctionNr++;
+ }
+ delete[] it->subFunctions;
+ }
+ delete[] it->signature;
+ }
}
uint Kernel::getSelectorNamesSize() const {
return _selectorNames.size();
}
-const Common::String &Kernel::getSelectorName(uint selector) const {
+const Common::String &Kernel::getSelectorName(uint selector) {
+ if (selector >= _selectorNames.size()) {
+ // This should only occur in games w/o a selector-table
+ // We need this for proper workaround tables
+ // TODO: maybe check, if there is a fixed selector-table and error() out in that case
+ for (uint loopSelector = _selectorNames.size(); loopSelector <= selector; ++loopSelector)
+ _selectorNames.push_back(Common::String::printf("<noname%d>", loopSelector));
+ }
+
+ // Ensure that the selector has a name
+ if (_selectorNames[selector].empty())
+ _selectorNames[selector] = Common::String::printf("<noname%d>", selector);
+
return _selectorNames[selector];
}
@@ -417,12 +81,10 @@ uint Kernel::getKernelNamesSize() const {
}
const Common::String &Kernel::getKernelName(uint number) const {
- // FIXME: The following check is a temporary workaround for
- // an issue leading to crashes when using the debugger's backtrace
- // command.
- static const Common::String invalid = "(invalid)";
+ // FIXME: The following check is a temporary workaround for an issue
+ // leading to crashes when using the debugger's backtrace command.
if (number >= _kernelNames.size())
- return invalid;
+ return _invalid;
return _kernelNames[number];
}
@@ -477,165 +139,241 @@ void Kernel::loadSelectorNames() {
}
}
-static void kernel_compile_signature(const char **s) {
- const char *src = *s;
- char *result;
- int ellipsis = 0;
- char v;
- int index = 0;
-
- if (!src)
- return; // NULL signature: Nothing to do
-
- result = (char *)malloc(strlen(*s) + 1);
-
- while (*src) {
- char c;
- v = 0;
-
- if (ellipsis) {
- error("Failed compiling kernel function signature '%s': non-terminal ellipsis '%c'", *s, *src);
- }
-
- do {
- char cc;
- cc = c = *src++;
- if (c >= 'A' || c <= 'Z')
- cc = c | KSIG_SPEC_SUM_DONE;
-
- switch (cc) {
- case KSIG_SPEC_LIST:
- v |= KSIG_LIST;
- break;
-
- case KSIG_SPEC_NODE:
- v |= KSIG_NODE;
- break;
-
- case KSIG_SPEC_REF:
- v |= KSIG_REF;
- break;
-
- case KSIG_SPEC_OBJECT:
- v |= KSIG_OBJECT;
- break;
-
- case KSIG_SPEC_ARITHMETIC:
- v |= KSIG_ARITHMETIC;
- break;
-
- case KSIG_SPEC_NULL:
- v |= KSIG_NULL;
- break;
-
- case KSIG_SPEC_ANY:
- v |= KSIG_ANY;
- break;
-
- case KSIG_SPEC_ELLIPSIS:
- v |= KSIG_ELLIPSIS;
- ellipsis = 1;
- break;
-
- default:
- error("INTERNAL ERROR when compiling kernel function signature '%s': (%02x) not understood (aka"
- " '%c')\n", *s, c, c);
+// this parses a written kernel signature into an internal memory format
+// [io] -> either integer or object
+// (io) -> optionally integer AND an object
+// (i) -> optional integer
+// . -> any type
+// i* -> optional multiple integers
+// .* -> any parameters afterwards (or none)
+static uint16 *parseKernelSignature(const char *kernelName, const char *writtenSig) {
+ const char *curPos;
+ char curChar;
+ uint16 *result = NULL;
+ uint16 *writePos = NULL;
+ int size = 0;
+ bool validType = false;
+ bool optionalType = false;
+ bool eitherOr = false;
+ bool optional = false;
+ bool hadOptional = false;
+
+ // No signature given? no signature out
+ if (!writtenSig)
+ return NULL;
+
+ // First, we check how many bytes the result will be
+ // we also check, if the written signature makes any sense
+ curPos = writtenSig;
+ while (*curPos) {
+ curChar = *curPos;
+ switch (curChar) {
+ case '[': // either or
+ if (eitherOr)
+ error("signature for k%s: '[' used within '[]'", kernelName);
+ eitherOr = true;
+ validType = false;
+ break;
+ case ']': // either or end
+ if (!eitherOr)
+ error("signature for k%s: ']' used without leading '['", kernelName);
+ if (!validType)
+ error("signature for k%s: '[]' does not surround valid type(s)", kernelName);
+ eitherOr = false;
+ validType = false;
+ size++;
+ break;
+ case '(': // optional
+ if (optional)
+ error("signature for k%s: '(' used within '()' brackets", kernelName);
+ if (eitherOr)
+ error("signature for k%s: '(' used within '[]' brackets", kernelName);
+ optional = true;
+ validType = false;
+ optionalType = false;
+ break;
+ case ')': // optional end
+ if (!optional)
+ error("signature for k%s: ')' used without leading '('", kernelName);
+ if (!optionalType)
+ error("signature for k%s: '()' does not to surround valid type(s)", kernelName);
+ optional = false;
+ validType = false;
+ hadOptional = true;
+ break;
+ case '0': // allowed types
+ case 'i':
+ case 'o':
+ case 'r':
+ case 'l':
+ case 'n':
+ case '.':
+ case '!':
+ if ((hadOptional) & (!optional))
+ error("signature for k%s: non-optional type may not follow optional type", kernelName);
+ validType = true;
+ if (optional)
+ optionalType = true;
+ if (!eitherOr)
+ size++;
+ break;
+ case '*': // accepts more of the same parameter (must be last char)
+ if (!validType) {
+ if ((writtenSig == curPos) || (*(curPos - 1) != ']'))
+ error("signature for k%s: a valid type must be in front of '*'", kernelName);
}
- } while (*src && (*src == KSIG_SPEC_ELLIPSIS || (c < 'a' && c != KSIG_SPEC_ANY)));
-
- // To handle sum types
- result[index++] = v;
- }
-
- result[index] = 0;
- *s = result; // Write back
-}
-
-void Kernel::mapFunctions() {
- int mapped = 0;
- int ignored = 0;
- uint functions_nr = _kernelNames.size();
-
- _kernelFuncs.resize(functions_nr);
-
- for (uint functnr = 0; functnr < functions_nr; functnr++) {
- int found = -1;
-
- // First, get the name, if known, of the kernel function with number functnr
- Common::String sought_name = _kernelNames[functnr];
-
- // Reset the table entry
- _kernelFuncs[functnr].fun = NULL;
- _kernelFuncs[functnr].signature = NULL;
- _kernelFuncs[functnr].orig_name = sought_name;
-
- if (sought_name.empty()) {
- // No name was given -> must be an unknown opcode
- warning("Kernel function %x unknown", functnr);
- _kernelFuncs[functnr].isDummy = true;
- continue;
- }
-
- // Don't map dummy functions - they will never be called
- if (sought_name == "Dummy") {
- _kernelFuncs[functnr].isDummy = true;
- continue;
+ if (eitherOr)
+ error("signature for k%s: '*' may not be inside '[]'", kernelName);
+ if (optional) {
+ if ((*(curPos + 1) != ')') || (*(curPos + 2) != 0))
+ error("signature for k%s: '*' may only be used for last type", kernelName);
+ } else {
+ if (*(curPos + 1) != 0)
+ error("signature for k%s: '*' may only be used for last type", kernelName);
+ }
+ break;
+ default:
+ error("signature for k%s: '%c' unknown", kernelName, *curPos);
}
+ curPos++;
+ }
- // If the name is known, look it up in kfunct_mappers. This table
- // maps kernel func names to actual function (pointers).
- for (uint seeker = 0; (found == -1) && kfunct_mappers[seeker].name; seeker++)
- if (sought_name == kfunct_mappers[seeker].name)
- found = seeker; // Found a kernel function with the correct name!
-
- if (found == -1) {
- // No match but a name was given -> stub
- warning("Kernel function %s[%x] unmapped", sought_name.c_str(), functnr);
- _kernelFuncs[functnr].isDummy = true;
- } else {
- // A match in kfunct_mappers was found
- if (kfunct_mappers[found].fun) {
- _kernelFuncs[functnr].fun = kfunct_mappers[found].fun;
- _kernelFuncs[functnr].signature = kfunct_mappers[found].signature;
- _kernelFuncs[functnr].isDummy = false;
- kernel_compile_signature(&(_kernelFuncs[functnr].signature));
- ++mapped;
- } else {
- //warning("Ignoring function %s\n", kfunct_mappers[found].name);
- ++ignored;
+ uint16 signature = 0;
+
+ // Now we allocate buffer with required size and fill it
+ result = new uint16[size + 1];
+ writePos = result;
+ curPos = writtenSig;
+ do {
+ curChar = *curPos;
+ if (!eitherOr) {
+ // not within either-or, check if next character forces output
+ switch (curChar) {
+ case 0:
+ case '[':
+ case '(':
+ case ')':
+ case 'i':
+ case 'o':
+ case 'r':
+ case 'l':
+ case 'n':
+ case '.':
+ case '!':
+ // and we also got some signature pending?
+ if (signature) {
+ if (!(signature & SIG_MAYBE_ANY))
+ error("signature for k%s: invalid ('!') may only get used in combination with a real type", kernelName);
+ if ((signature & SIG_IS_INVALID) && ((signature & SIG_MAYBE_ANY) == (SIG_TYPE_NULL | SIG_TYPE_INTEGER)))
+ error("signature for k%s: invalid ('!') should not be used on exclusive null/integer type", kernelName);
+ if (optional) {
+ signature |= SIG_IS_OPTIONAL;
+ if (curChar != ')')
+ signature |= SIG_NEEDS_MORE;
+ }
+ *writePos = signature;
+ writePos++;
+ signature = 0;
+ }
}
}
- } // for all functions requesting to be mapped
+ switch (curChar) {
+ case '[': // either or
+ eitherOr = true;
+ break;
+ case ']': // either or end
+ eitherOr = false;
+ break;
+ case '(': // optional
+ optional = true;
+ break;
+ case ')': // optional end
+ optional = false;
+ break;
+ case '0':
+ if (signature & SIG_TYPE_NULL)
+ error("signature for k%s: NULL ('0') specified more than once", kernelName);
+ signature |= SIG_TYPE_NULL;
+ break;
+ case 'i':
+ if (signature & SIG_TYPE_INTEGER)
+ error("signature for k%s: integer ('i') specified more than once", kernelName);
+ signature |= SIG_TYPE_INTEGER | SIG_TYPE_NULL;
+ break;
+ case 'o':
+ if (signature & SIG_TYPE_OBJECT)
+ error("signature for k%s: object ('o') specified more than once", kernelName);
+ signature |= SIG_TYPE_OBJECT;
+ break;
+ case 'r':
+ if (signature & SIG_TYPE_REFERENCE)
+ error("signature for k%s: reference ('r') specified more than once", kernelName);
+ signature |= SIG_TYPE_REFERENCE;
+ break;
+ case 'l':
+ if (signature & SIG_TYPE_LIST)
+ error("signature for k%s: list ('l') specified more than once", kernelName);
+ signature |= SIG_TYPE_LIST;
+ break;
+ case 'n':
+ if (signature & SIG_TYPE_NODE)
+ error("signature for k%s: node ('n') specified more than once", kernelName);
+ signature |= SIG_TYPE_NODE;
+ break;
+ case '.':
+ if (signature & SIG_MAYBE_ANY)
+ error("signature for k%s: maybe-any ('.') shouldn't get specified with other types in front of it", kernelName);
+ signature |= SIG_MAYBE_ANY;
+ break;
+ case '!':
+ if (signature & SIG_IS_INVALID)
+ error("signature for k%s: invalid ('!') specified more than once", kernelName);
+ signature |= SIG_IS_INVALID;
+ break;
+ case '*': // accepts more of the same parameter
+ signature |= SIG_MORE_MAY_FOLLOW;
+ break;
+ default:
+ break;
+ }
+ curPos++;
+ } while (curChar);
- debugC(2, kDebugLevelVM, "Handled %d/%d kernel functions, mapping %d and ignoring %d.",
- mapped + ignored, _kernelNames.size(), mapped, ignored);
+ // Write terminator
+ *writePos = 0;
- return;
+ return result;
}
-int Kernel::findRegType(reg_t reg) {
- // No segment? Must be arithmetic
+uint16 Kernel::findRegType(reg_t reg) {
+ // No segment? Must be integer
if (!reg.segment)
- return reg.offset ? KSIG_ARITHMETIC : KSIG_ARITHMETIC | KSIG_NULL;
+ return SIG_TYPE_INTEGER | (reg.offset ? 0 : SIG_TYPE_NULL);
+
+ if (reg.segment == 0xFFFF)
+ return SIG_TYPE_UNINITIALIZED;
// Otherwise it's an object
SegmentObj *mobj = _segMan->getSegmentObj(reg.segment);
if (!mobj)
- return 0; // Invalid
+ return SIG_TYPE_ERROR;
+ uint16 result = 0;
if (!mobj->isValidOffset(reg.offset))
- warning("[KERN] ref %04x:%04x is invalid", PRINT_REG(reg));
+ result |= SIG_IS_INVALID;
switch (mobj->getType()) {
case SEG_TYPE_SCRIPT:
- if (reg.offset <= (*(Script *)mobj)._bufSize &&
+ if (reg.offset <= (*(Script *)mobj).getBufSize() &&
reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET &&
- RAW_IS_OBJECT((*(Script *)mobj)._buf + reg.offset)) {
- return ((Script *)mobj)->getObject(reg.offset) ? KSIG_OBJECT : KSIG_REF;
+ RAW_IS_OBJECT((*(Script *)mobj).getBuf(reg.offset)) ) {
+ result |= ((Script *)mobj)->getObject(reg.offset) ? SIG_TYPE_OBJECT : SIG_TYPE_REFERENCE;
} else
- return KSIG_REF;
+ result |= SIG_TYPE_REFERENCE;
+ break;
case SEG_TYPE_CLONES:
- return KSIG_OBJECT;
+ result |= SIG_TYPE_OBJECT;
+ break;
case SEG_TYPE_LOCALS:
case SEG_TYPE_STACK:
case SEG_TYPE_SYS_STRINGS:
@@ -645,58 +383,372 @@ int Kernel::findRegType(reg_t reg) {
case SEG_TYPE_ARRAY:
case SEG_TYPE_STRING:
#endif
- return KSIG_REF;
+ result |= SIG_TYPE_REFERENCE;
+ break;
case SEG_TYPE_LISTS:
- return KSIG_LIST;
+ result |= SIG_TYPE_LIST;
+ break;
case SEG_TYPE_NODES:
- return KSIG_NODE;
+ result |= SIG_TYPE_NODE;
+ break;
default:
- return 0;
+ return SIG_TYPE_ERROR;
}
+ return result;
}
-bool Kernel::signatureMatch(const char *sig, int argc, const reg_t *argv) {
- // Always "match" if no signature is given
- if (!sig)
- return true;
+struct SignatureDebugType {
+ uint16 typeCheck;
+ const char *text;
+};
- while (*sig && argc) {
- if ((*sig & KSIG_ANY) != KSIG_ANY) {
- int type = findRegType(*argv);
+static const SignatureDebugType signatureDebugTypeList[] = {
+ { SIG_TYPE_NULL, "null" },
+ { SIG_TYPE_INTEGER, "integer" },
+ { SIG_TYPE_UNINITIALIZED, "uninitialized" },
+ { SIG_TYPE_OBJECT, "object" },
+ { SIG_TYPE_REFERENCE, "reference" },
+ { SIG_TYPE_LIST, "list" },
+ { SIG_TYPE_NODE, "node" },
+ { SIG_TYPE_ERROR, "error" },
+ { SIG_IS_INVALID, "invalid" },
+ { 0, NULL }
+};
- if (!type) {
- warning("[KERN] Could not determine type of ref %04x:%04x; failing signature check", PRINT_REG(*argv));
- return false;
- }
+static void kernelSignatureDebugType(const uint16 type) {
+ bool firstPrint = true;
- if (!(type & *sig)) {
- warning("kernel_matches_signature: %d args left, is %d, should be %d", argc, type, *sig);
- return false;
- }
+ const SignatureDebugType *list = signatureDebugTypeList;
+ while (list->typeCheck) {
+ if (type & list->typeCheck) {
+ if (!firstPrint)
+ printf(", ");
+ printf("%s", list->text);
+ firstPrint = false;
+ }
+ list++;
+ }
+}
+// Shows kernel call signature and current arguments for debugging purposes
+void Kernel::signatureDebug(const uint16 *sig, int argc, const reg_t *argv) {
+ int argnr = 0;
+ while (*sig || argc) {
+ printf("parameter %d: ", argnr++);
+ if (argc) {
+ reg_t parameter = *argv;
+ printf("%04x:%04x (", PRINT_REG(parameter));
+ int regType = findRegType(parameter);
+ if (regType)
+ kernelSignatureDebugType(regType);
+ else
+ printf("unknown type of %04x:%04x", PRINT_REG(parameter));
+ printf(")");
+ argv++;
+ argc--;
+ } else {
+ printf("not passed");
+ }
+ if (*sig) {
+ const uint16 signature = *sig;
+ if ((signature & SIG_MAYBE_ANY) == SIG_MAYBE_ANY) {
+ printf(", may be any");
+ } else {
+ printf(", should be ");
+ kernelSignatureDebugType(signature);
+ }
+ if (signature & SIG_IS_OPTIONAL)
+ printf(" (optional)");
+ if (signature & SIG_NEEDS_MORE)
+ printf(" (needs more)");
+ if (signature & SIG_MORE_MAY_FOLLOW)
+ printf(" (more may follow)");
+ sig++;
}
- if (!(*sig & KSIG_ELLIPSIS))
- ++sig;
- ++argv;
- --argc;
+ printf("\n");
}
+}
+
+bool Kernel::signatureMatch(const uint16 *sig, int argc, const reg_t *argv) {
+ uint16 nextSig = *sig;
+ uint16 curSig = nextSig;
+ while (nextSig && argc) {
+ curSig = nextSig;
+ int type = findRegType(*argv);
+
+ if ((type & SIG_IS_INVALID) && (!(curSig & SIG_IS_INVALID)))
+ return false; // pointer is invalid and signature doesn't allow that?
- if (argc) {
- warning("kernel_matches_signature: too many arguments");
- return false; // Too many arguments
+ if (!((type & ~SIG_IS_INVALID) & curSig))
+ return false; // type mismatch
+
+ if (!(curSig & SIG_MORE_MAY_FOLLOW)) {
+ sig++;
+ nextSig = *sig;
+ } else {
+ nextSig |= SIG_IS_OPTIONAL; // more may follow -> assumes followers are optional
+ }
+ argv++;
+ argc--;
}
- if (*sig == 0 || (*sig & KSIG_ELLIPSIS))
+
+ // Too many arguments?
+ if (argc)
+ return false;
+ // Signature end reached?
+ if (nextSig == 0)
return true;
- warning("kernel_matches_signature: too few arguments");
+ // current parameter is optional?
+ if (curSig & SIG_IS_OPTIONAL) {
+ // yes, check if nothing more is required
+ if (!(curSig & SIG_NEEDS_MORE))
+ return true;
+ } else {
+ // no, check if next parameter is optional
+ if (nextSig & SIG_IS_OPTIONAL)
+ return true;
+ }
+ // Too few arguments or more optional arguments required
return false;
}
-void Kernel::setDefaultKernelNames(Common::String gameId) {
- _kernelNames = Common::StringArray(sci_default_knames, SCI_KNAMES_DEFAULT_ENTRIES_NR);
+void Kernel::mapFunctions() {
+ int mapped = 0;
+ int ignored = 0;
+ uint functionCount = _kernelNames.size();
+ byte platformMask = 0;
+ SciVersion myVersion = getSciVersion();
+
+ switch (g_sci->getPlatform()) {
+ case Common::kPlatformPC:
+ platformMask = SIGFOR_DOS;
+ break;
+ case Common::kPlatformPC98:
+ platformMask = SIGFOR_PC98;
+ break;
+ case Common::kPlatformWindows:
+ platformMask = SIGFOR_WIN;
+ break;
+ case Common::kPlatformMacintosh:
+ platformMask = SIGFOR_MAC;
+ break;
+ case Common::kPlatformAmiga:
+ platformMask = SIGFOR_AMIGA;
+ break;
+ case Common::kPlatformAtariST:
+ platformMask = SIGFOR_ATARI;
+ break;
+ default:
+ break;
+ }
+
+ _kernelFuncs.resize(functionCount);
+
+ for (uint id = 0; id < functionCount; id++) {
+ // First, get the name, if known, of the kernel function with number functnr
+ Common::String kernelName = _kernelNames[id];
+
+ // Reset the table entry
+ _kernelFuncs[id].function = NULL;
+ _kernelFuncs[id].signature = NULL;
+ _kernelFuncs[id].name = NULL;
+ _kernelFuncs[id].workarounds = NULL;
+ _kernelFuncs[id].subFunctions = NULL;
+ _kernelFuncs[id].subFunctionCount = 0;
+ _kernelFuncs[id].debugLogging = false;
+ if (kernelName.empty()) {
+ // No name was given -> must be an unknown opcode
+ warning("Kernel function %x unknown", id);
+ continue;
+ }
+
+ // Don't map dummy functions - they will never be called
+ if (kernelName == "Dummy") {
+ _kernelFuncs[id].function = kDummy;
+ continue;
+ }
+
+ // If the name is known, look it up in s_kernelMap. This table
+ // maps kernel func names to actual function (pointers).
+ SciKernelMapEntry *kernelMap = s_kernelMap;
+ bool nameMatch = false;
+ while (kernelMap->name) {
+ if (kernelName == kernelMap->name) {
+ if ((kernelMap->fromVersion == SCI_VERSION_NONE) || (kernelMap->fromVersion <= myVersion))
+ if ((kernelMap->toVersion == SCI_VERSION_NONE) || (kernelMap->toVersion >= myVersion))
+ if (platformMask & kernelMap->forPlatform)
+ break;
+ nameMatch = true;
+ }
+ kernelMap++;
+ }
+
+ if (kernelMap->name) {
+ // A match was found
+ _kernelFuncs[id].function = kernelMap->function;
+ _kernelFuncs[id].name = kernelMap->name;
+ _kernelFuncs[id].signature = parseKernelSignature(kernelMap->name, kernelMap->signature);
+ _kernelFuncs[id].workarounds = kernelMap->workarounds;
+ if (kernelMap->subFunctions) {
+ // Get version for subfunction identification
+ SciVersion mySubVersion = (SciVersion)kernelMap->function(NULL, 0, NULL).offset;
+ // Now check whats the highest subfunction-id for this version
+ const SciKernelMapSubEntry *kernelSubMap = kernelMap->subFunctions;
+ uint16 subFunctionCount = 0;
+ while (kernelSubMap->function) {
+ if ((kernelSubMap->fromVersion == SCI_VERSION_NONE) || (kernelSubMap->fromVersion <= mySubVersion))
+ if ((kernelSubMap->toVersion == SCI_VERSION_NONE) || (kernelSubMap->toVersion >= mySubVersion))
+ if (subFunctionCount <= kernelSubMap->id)
+ subFunctionCount = kernelSubMap->id + 1;
+ kernelSubMap++;
+ }
+ if (!subFunctionCount)
+ error("k%s[%x]: no subfunctions found for requested version", kernelName.c_str(), id);
+ // Now allocate required memory and go through it again
+ _kernelFuncs[id].subFunctionCount = subFunctionCount;
+ KernelSubFunction *subFunctions = new KernelSubFunction[subFunctionCount];
+ _kernelFuncs[id].subFunctions = subFunctions;
+ memset(subFunctions, 0, sizeof(KernelSubFunction) * subFunctionCount);
+ // And fill this info out
+ kernelSubMap = kernelMap->subFunctions;
+ uint kernelSubNr = 0;
+ while (kernelSubMap->function) {
+ if ((kernelSubMap->fromVersion == SCI_VERSION_NONE) || (kernelSubMap->fromVersion <= mySubVersion))
+ if ((kernelSubMap->toVersion == SCI_VERSION_NONE) || (kernelSubMap->toVersion >= mySubVersion)) {
+ uint subId = kernelSubMap->id;
+ if (!subFunctions[subId].function) {
+ subFunctions[subId].function = kernelSubMap->function;
+ subFunctions[subId].name = kernelSubMap->name;
+ subFunctions[subId].workarounds = kernelSubMap->workarounds;
+ if (kernelSubMap->signature) {
+ subFunctions[subId].signature = parseKernelSignature(kernelSubMap->name, kernelSubMap->signature);
+ } else {
+ // we go back the submap to find the previous signature for that kernel call
+ const SciKernelMapSubEntry *kernelSubMapBack = kernelSubMap;
+ uint kernelSubLeft = kernelSubNr;
+ while (kernelSubLeft) {
+ kernelSubLeft--;
+ kernelSubMapBack--;
+ if (kernelSubMapBack->name == kernelSubMap->name) {
+ if (kernelSubMapBack->signature) {
+ subFunctions[subId].signature = parseKernelSignature(kernelSubMap->name, kernelSubMapBack->signature);
+ break;
+ }
+ }
+ }
+ if (!subFunctions[subId].signature)
+ error("k%s: no previous signatures", kernelSubMap->name);
+ }
+ }
+ }
+ kernelSubMap++;
+ kernelSubNr++;
+ }
+ }
+ ++mapped;
+ } else {
+ if (nameMatch)
+ error("k%s[%x]: not found for this version/platform", kernelName.c_str(), id);
+ // No match but a name was given -> stub
+ warning("k%s[%x]: unmapped", kernelName.c_str(), id);
+ _kernelFuncs[id].function = kStub;
+ }
+ } // for all functions requesting to be mapped
+
+ debugC(2, kDebugLevelVM, "Handled %d/%d kernel functions, mapping %d and ignoring %d.",
+ mapped + ignored, _kernelNames.size(), mapped, ignored);
+
+ return;
+}
+
+bool Kernel::debugSetFunction(const char *kernelName, int logging, int breakpoint) {
+ if (strcmp(kernelName, "*")) {
+ for (uint id = 0; id < _kernelFuncs.size(); id++) {
+ if (_kernelFuncs[id].name) {
+ if (strcmp(kernelName, _kernelFuncs[id].name) == 0) {
+ if (_kernelFuncs[id].subFunctions) {
+ // sub-functions available and main name matched, in that case set logging of all sub-functions
+ KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions;
+ uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount;
+ for (uint subId = 0; subId < kernelSubCallCount; subId++) {
+ if (kernelSubCall->function) {
+ if (logging != -1)
+ kernelSubCall->debugLogging = logging == 1 ? true : false;
+ if (breakpoint != -1)
+ kernelSubCall->debugBreakpoint = breakpoint == 1 ? true : false;
+ }
+ kernelSubCall++;
+ }
+ return true;
+ }
+ // function name matched, set for this one and exit
+ if (logging != -1)
+ _kernelFuncs[id].debugLogging = logging == 1 ? true : false;
+ if (breakpoint != -1)
+ _kernelFuncs[id].debugBreakpoint = breakpoint == 1 ? true : false;
+ return true;
+ } else {
+ // main name was not matched
+ if (_kernelFuncs[id].subFunctions) {
+ // Sub-Functions available
+ KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions;
+ uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount;
+ for (uint subId = 0; subId < kernelSubCallCount; subId++) {
+ if (kernelSubCall->function) {
+ if (strcmp(kernelName, kernelSubCall->name) == 0) {
+ // sub-function name matched, set for this one and exit
+ if (logging != -1)
+ kernelSubCall->debugLogging = logging == 1 ? true : false;
+ if (breakpoint != -1)
+ kernelSubCall->debugBreakpoint = breakpoint == 1 ? true : false;
+ return true;
+ }
+ }
+ kernelSubCall++;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ // Set debugLogging for all calls
+ for (uint id = 0; id < _kernelFuncs.size(); id++) {
+ if (_kernelFuncs[id].name) {
+ if (!_kernelFuncs[id].subFunctions) {
+ // No sub-functions, enable actual kernel function
+ if (logging != -1)
+ _kernelFuncs[id].debugLogging = logging == 1 ? true : false;
+ if (breakpoint != -1)
+ _kernelFuncs[id].debugBreakpoint = breakpoint == 1 ? true : false;
+ } else {
+ // Sub-Functions available, enable those too
+ KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions;
+ uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount;
+ for (uint subId = 0; subId < kernelSubCallCount; subId++) {
+ if (kernelSubCall->function) {
+ if (logging != -1)
+ kernelSubCall->debugLogging = logging == 1 ? true : false;
+ if (breakpoint != -1)
+ kernelSubCall->debugBreakpoint = breakpoint == 1 ? true : false;
+ }
+ kernelSubCall++;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void Kernel::setDefaultKernelNames(GameFeatures *features) {
+ _kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames));
// Some (later) SCI versions replaced CanBeHere by CantBeHere
- if (_selectorCache.cantBeHere != -1)
- _kernelNames[0x4d] = "CantBeHere";
+ if (_selectorCache.cantBeHere != -1) {
+ // hoyle 3 has cantBeHere selector but is assuming to call kCanBeHere
+ if (g_sci->getGameId() != GID_HOYLE3)
+ _kernelNames[0x4d] = "CantBeHere";
+ }
switch (getSciVersion()) {
case SCI_VERSION_0_EARLY:
@@ -727,18 +779,27 @@ void Kernel::setDefaultKernelNames(Common::String gameId) {
break;
case SCI_VERSION_1_1:
- // In KQ6 CD, the empty kSetSynonyms function has been replaced
- // with kPortrait. In KQ6 Mac, kPlayBack has been replaced by
- // kShowMovie.
- if (gameId == "kq6") {
- if (g_sci->getPlatform() == Common::kPlatformMacintosh)
- _kernelNames[0x84] = "ShowMovie";
- else
+ // In SCI1.1, kSetSynonyms is an empty function
+ _kernelNames[0x26] = "Empty";
+
+ if (g_sci->getGameId() == GID_KQ6) {
+ // In the Windows version of KQ6 CD, the empty kSetSynonyms
+ // function has been replaced with kPortrait. In KQ6 Mac,
+ // kPlayBack has been replaced by kShowMovie.
+ if (g_sci->getPlatform() == Common::kPlatformWindows)
_kernelNames[0x26] = "Portrait";
+ else if (g_sci->getPlatform() == Common::kPlatformMacintosh)
+ _kernelNames[0x84] = "ShowMovie";
+ } else if (g_sci->getGameId() == GID_QFG4 && g_sci->isDemo()) {
+ _kernelNames[0x7b] = "RemapColors"; // QFG4 Demo has this SCI2 function instead of StrSplit
}
_kernelNames[0x71] = "PalVary";
- _kernelNames[0x7c] = "Message";
+
+ // At least EcoQuest 1 demo uses kGetMessage instead of kMessage.
+ // Detect which function to use.
+ if (features->detectMessageFunctionType() == SCI_VERSION_1_1)
+ _kernelNames[0x7c] = "Message";
break;
default:
@@ -747,20 +808,52 @@ void Kernel::setDefaultKernelNames(Common::String gameId) {
}
}
-bool Kernel::loadKernelNames(Common::String gameId) {
+#ifdef ENABLE_SCI32
+
+enum {
+ kKernelEntriesSci2 = 0x8b,
+ kKernelEntriesGk2Demo = 0xa0,
+ kKernelEntriesSci21 = 0x9d
+};
+
+void Kernel::setKernelNamesSci2() {
+ _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
+}
+
+void Kernel::setKernelNamesSci21(GameFeatures *features) {
+ // Some SCI games use a modified SCI2 kernel table instead of the
+ // SCI2.1 kernel table. The GK2 demo does this as well as at least
+ // one version of KQ7 (1.4). We detect which version to use based on
+ // how kDoSound is called from Sound::play().
+
+ // This is interesting because they all have the same interpreter
+ // version (2.100.002), yet they would not be compatible with other
+ // games of the same interpreter.
+
+ if (features->detectSci21KernelType() == SCI_VERSION_2) {
+ _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
+ // OnMe is IsOnMe here, but they should be compatible
+ _kernelNames[0x23] = "Robot"; // Graph in SCI2
+ _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
+ } else
+ _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21);
+}
+
+#endif
+
+void Kernel::loadKernelNames(GameFeatures *features) {
_kernelNames.clear();
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2_1)
- setKernelNamesSci21();
+ setKernelNamesSci21(features);
else if (getSciVersion() == SCI_VERSION_2)
setKernelNamesSci2();
else
#endif
- setDefaultKernelNames(gameId);
+ setDefaultKernelNames(features);
mapFunctions();
- return true;
}
Common::String Kernel::lookupText(reg_t address, int index) {
@@ -769,31 +862,50 @@ Common::String Kernel::lookupText(reg_t address, int index) {
if (address.segment)
return _segMan->getString(address);
- else {
- int textlen;
- int _index = index;
- textres = _resMan->findResource(ResourceId(kResourceTypeText, address.offset), 0);
-
- if (!textres) {
- error("text.%03d not found", address.offset);
- return NULL; /* Will probably segfault */
- }
- textlen = textres->size;
- seeker = (char *) textres->data;
+ int textlen;
+ int _index = index;
+ textres = _resMan->findResource(ResourceId(kResourceTypeText, address.offset), 0);
- while (index--)
- while ((textlen--) && (*seeker++))
- ;
+ if (!textres) {
+ error("text.%03d not found", address.offset);
+ return NULL; /* Will probably segfault */
+ }
- if (textlen)
- return seeker;
- else {
- error("Index %d out of bounds in text.%03d", _index, address.offset);
- return NULL;
- }
+ textlen = textres->size;
+ seeker = (char *) textres->data;
+
+ while (index--)
+ while ((textlen--) && (*seeker++))
+ ;
+
+ if (textlen)
+ return seeker;
+
+ error("Index %d out of bounds in text.%03d", _index, address.offset);
+ return NULL;
+}
+// TODO: script_adjust_opcode_formats should probably be part of the
+// constructor (?) of a VirtualMachine or a ScriptManager class.
+void script_adjust_opcode_formats() {
+ if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
+ g_opcode_formats[op_lofsa][0] = Script_Offset;
+ g_opcode_formats[op_lofss][0] = Script_Offset;
}
+
+#ifdef ENABLE_SCI32
+ // In SCI32, some arguments are now words instead of bytes
+ if (getSciVersion() >= SCI_VERSION_2) {
+ g_opcode_formats[op_calle][2] = Script_Word;
+ g_opcode_formats[op_callk][1] = Script_Word;
+ g_opcode_formats[op_super][1] = Script_Word;
+ g_opcode_formats[op_send][0] = Script_Word;
+ g_opcode_formats[op_self][0] = Script_Word;
+ g_opcode_formats[op_call][1] = Script_Word;
+ g_opcode_formats[op_callb][1] = Script_Word;
+ }
+#endif
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index 7717743e19..b6247b46f1 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -31,7 +31,7 @@
#include "common/rect.h"
#include "common/str-array.h"
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
+#include "sci/engine/selector.h"
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/engine/vm.h"
@@ -39,6 +39,8 @@ namespace Sci {
struct Node; // from segment.h
struct List; // from segment.h
+struct SelectorCache; // from selector.h
+struct SciWorkaroundEntry; // from workarounds.h
/**
* @defgroup VocabularyResources Vocabulary resources in SCI
@@ -92,82 +94,69 @@ struct List; // from segment.h
//@{
//#define DEBUG_PARSER // enable for parser debugging
-//#define DISABLE_VALIDATIONS // enable to stop validation checks
// ---- Kernel signatures -----------------------------------------------------
-#define KSIG_TERMINATOR 0
-
-// Uncompiled signatures
-#define KSIG_SPEC_ARITMETIC 'i'
-#define KSIG_SPEC_LIST 'l'
-#define KSIG_SPEC_NODE 'n'
-#define KSIG_SPEC_OBJECT 'o'
-#define KSIG_SPEC_REF 'r' // Said Specs and strings
-#define KSIG_SPEC_ARITHMETIC 'i'
-#define KSIG_SPEC_NULL 'z'
-#define KSIG_SPEC_ANY '.'
-#define KSIG_SPEC_ELLIPSIS '*' // Arbitrarily more TYPED arguments
-
-#define KSIG_SPEC_SUM_DONE ('a' - 'A') // Use small letters to indicate end of sum type
-/* Use capital letters for sum types, e.g.
-** "LNoLr" for a function which takes two arguments:
-** (1) list, node or object
-** (2) list or ref
-*/
-
-// Compiled signatures
-#define KSIG_LIST 0x01
-#define KSIG_NODE 0x02
-#define KSIG_OBJECT 0x04
-#define KSIG_REF 0x08
-#define KSIG_ARITHMETIC 0x10
-
-#define KSIG_NULL 0x40
-#define KSIG_ANY 0x5f
-#define KSIG_ELLIPSIS 0x80
+
+// internal kernel signature data
+enum {
+ SIG_TYPE_NULL = 0x01, // may be 0:0 [0]
+ SIG_TYPE_INTEGER = 0x02, // may be 0:* [i], automatically also allows null
+ SIG_TYPE_UNINITIALIZED = 0x04, // may be FFFF:* -> not allowable, only used for comparison
+ SIG_TYPE_OBJECT = 0x08, // may be object [o]
+ SIG_TYPE_REFERENCE = 0x10, // may be reference [r]
+ SIG_TYPE_LIST = 0x20, // may be list [l]
+ SIG_TYPE_NODE = 0x40, // may be node [n]
+ SIG_TYPE_ERROR = 0x80, // happens, when there is a identification error - only used for comparison
+ SIG_IS_INVALID = 0x100, // ptr is invalid [!] -> invalid offset
+ SIG_IS_OPTIONAL = 0x200, // is optional
+ SIG_NEEDS_MORE = 0x400, // needs at least one additional parameter following
+ SIG_MORE_MAY_FOLLOW = 0x800 // may have more parameters of the same type following
+};
+
+// this does not include SIG_TYPE_UNINITIALIZED, because we can not allow uninitialized values anywhere
+#define SIG_MAYBE_ANY (SIG_TYPE_NULL | SIG_TYPE_INTEGER | SIG_TYPE_OBJECT | SIG_TYPE_REFERENCE | SIG_TYPE_LIST | SIG_TYPE_NODE)
+
// ----------------------------------------------------------------------------
/* Generic description: */
-typedef reg_t KernelFunc(EngineState *s, int argc, reg_t *argv);
-
-struct KernelFuncWithSignature {
- KernelFunc *fun; /**< The actual function */
- const char *signature; /**< KernelFunc signature */
- Common::String orig_name; /**< Original name, in case we couldn't map it */
- bool isDummy;
+typedef reg_t KernelFunctionCall(EngineState *s, int argc, reg_t *argv);
+
+struct KernelSubFunction {
+ KernelFunctionCall *function;
+ const char *name;
+ uint16 *signature;
+ const SciWorkaroundEntry *workarounds;
+ bool debugLogging;
+ bool debugBreakpoint;
};
-enum AutoDetectedFeatures {
- kFeatureOldScriptHeader = 1 << 0
+struct KernelFunction {
+ KernelFunctionCall *function;
+ const char *name;
+ uint16 *signature;
+ const SciWorkaroundEntry *workarounds;
+ KernelSubFunction *subFunctions;
+ uint16 subFunctionCount;
+ bool debugLogging;
+ bool debugBreakpoint;
};
class Kernel {
public:
/**
- * Initializes the SCI kernel
+ * Initializes the SCI kernel.
*/
Kernel(ResourceManager *resMan, SegManager *segMan);
~Kernel();
uint getSelectorNamesSize() const;
- const Common::String &getSelectorName(uint selector) const;
+ const Common::String &getSelectorName(uint selector);
uint getKernelNamesSize() const;
const Common::String &getKernelName(uint number) const;
/**
- * Loads the kernel function names.
- *
- * This function reads the kernel function name table from resource_map,
- * and fills the _kernelNames array with them.
- * The resulting list has the same format regardless of the format of the
- * name table of the resource (the format changed between version 0 and 1).
- * @return true on success, false on failure
- */
- bool loadKernelNames(Common::String gameId);
-
- /**
- * Determines the selector ID of a selector by its name
+ * Determines the selector ID of a selector by its name.
* @param selectorName Name of the selector to look up
* @return The appropriate selector ID, or -1 on error
*/
@@ -178,22 +167,24 @@ public:
void dumpScriptObject(char *data, int seeker, int objsize);
void dumpScriptClass(char *data, int seeker, int objsize);
- SelectorCache _selectorCache; /**< Shortcut list for important selectors */
- typedef Common::Array<KernelFuncWithSignature> KernelFuncsContainer;
- KernelFuncsContainer _kernelFuncs; /**< Table of kernel functions */
+ SelectorCache _selectorCache; /**< Shortcut list for important selectors. */
+ typedef Common::Array<KernelFunction> KernelFunctionArray;
+ KernelFunctionArray _kernelFuncs; /**< Table of kernel functions. */
/**
* Determines whether a list of registers matches a given signature.
* If no signature is given (i.e., if sig is NULL), this is always
* treated as a match.
*
- * @param segMan pointer to the segment manager
* @param sig signature to test against
* @param argc number of arguments to test
* @param argv argument list
* @return true if the signature was matched, false otherwise
*/
- bool signatureMatch(const char *sig, int argc, const reg_t *argv);
+ bool signatureMatch(const uint16 *sig, int argc, const reg_t *argv);
+
+ // Prints out debug information in case a signature check fails
+ void signatureDebug(const uint16 *sig, int argc, const reg_t *argv);
/**
* Determines the type of the object indicated by reg.
@@ -202,15 +193,15 @@ public:
* KSIG_INVALID set if the type of reg can be determined, but is invalid.
* 0 on error.
*/
- int findRegType(reg_t reg);
+ uint16 findRegType(reg_t reg);
/******************** Text functionality ********************/
/**
- * Looks up text referenced by scripts
- * SCI uses two values to reference to text: An address, and an index. The address
- * determines whether the text should be read from a resource file, or from the heap,
- * while the index either refers to the number of the string in the specified source,
- * or to a relative position inside the text.
+ * Looks up text referenced by scripts.
+ * SCI uses two values to reference to text: An address, and an index. The
+ * address determines whether the text should be read from a resource file,
+ * or from the heap, while the index either refers to the number of the
+ * string in the specified source, or to a relative position inside the text.
*
* @param address The address to look up
* @param index The relative index
@@ -218,22 +209,37 @@ public:
*/
Common::String lookupText(reg_t address, int index);
+ /**
+ * Loads the kernel function names.
+ *
+ * This function reads the kernel function name table from resource_map,
+ * and fills the _kernelNames array with them.
+ * The resulting list has the same format regardless of the format of the
+ * name table of the resource (the format changed between version 0 and 1).
+ */
+ void loadKernelNames(GameFeatures *features);
+
+ /**
+ * Sets debug flags for a kernel function
+ */
+ bool debugSetFunction(const char *kernelName, int logging, int breakpoint);
+
private:
/**
- * Sets the default kernel function names, based on the SCI version used
+ * Sets the default kernel function names, based on the SCI version used.
*/
- void setDefaultKernelNames(Common::String gameId);
+ void setDefaultKernelNames(GameFeatures *features);
#ifdef ENABLE_SCI32
/**
- * Sets the default kernel function names to the SCI2 kernel functions
+ * Sets the default kernel function names to the SCI2 kernel functions.
*/
void setKernelNamesSci2();
/**
- * Sets the default kernel function names to the SCI2.1 kernel functions
+ * Sets the default kernel function names to the SCI2.1 kernel functions.
*/
- void setKernelNamesSci21();
+ void setKernelNamesSci21(GameFeatures *features);
#endif
/**
@@ -248,36 +254,26 @@ private:
Common::StringArray checkStaticSelectorNames();
/**
- * Maps special selectors
+ * Maps special selectors.
*/
void mapSelectors();
/**
- * Maps kernel functions
+ * Maps kernel functions.
*/
void mapFunctions();
ResourceManager *_resMan;
SegManager *_segMan;
- uint32 features;
// Kernel-related lists
Common::StringArray _selectorNames;
Common::StringArray _kernelNames;
+
+ const Common::String _invalid;
};
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-/******************** Misc functions ********************/
-
-/**
- * Get all sound events, apply their changes to the heap
- */
-void process_sound_events(EngineState *s);
-
-/******************** Constants ********************/
-#endif
-
-/* Maximum length of a savegame name (including terminator character) */
+/* Maximum length of a savegame name (including terminator character). */
#define SCI_MAX_SAVENAME_LENGTH 0x24
/******************** Kernel functions ********************/
@@ -414,6 +410,7 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv);
reg_t kDoSync(EngineState *s, int argc, reg_t *argv);
reg_t kMemorySegment(EngineState *s, int argc, reg_t *argv);
reg_t kIntersections(EngineState *s, int argc, reg_t *argv);
+reg_t kMergePoly(EngineState *s, int argc, reg_t *argv);
reg_t kResCheck(EngineState *s, int argc, reg_t *argv);
reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv);
reg_t kShowMovie(EngineState *s, int argc, reg_t *argv);
@@ -422,6 +419,10 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv);
reg_t kPlatform(EngineState *s, int argc, reg_t *argv);
reg_t kTextColors(EngineState *s, int argc, reg_t *argv);
reg_t kTextFonts(EngineState *s, int argc, reg_t *argv);
+reg_t kDummy(EngineState *s, int argc, reg_t *argv);
+reg_t kEmpty(EngineState *s, int argc, reg_t *argv);
+reg_t kStub(EngineState *s, int argc, reg_t *argv);
+reg_t kStubNull(EngineState *s, int argc, reg_t *argv);
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
@@ -429,6 +430,8 @@ reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv);
reg_t kArray(EngineState *s, int argc, reg_t *argv);
reg_t kListAt(EngineState *s, int argc, reg_t *argv);
reg_t kString(EngineState *s, int argc, reg_t *argv);
+reg_t kMulDiv(EngineState *s, int argc, reg_t *argv);
+reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv);
// "Screen items" in SCI32 are views
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv);
reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
@@ -450,10 +453,91 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv);
reg_t kInPolygon(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
+reg_t kText(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
reg_t kList(EngineState *s, int argc, reg_t *argv);
reg_t kRobot(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv);
+reg_t kCD(EngineState *s, int argc, reg_t *argv);
+reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv);
+
+reg_t kAddBefore(EngineState *s, int argc, reg_t *argv);
+reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv);
+reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv);
+#endif
+reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundPlay(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundRestore(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundDispose(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundMute(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundStop(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundStopAll(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundPause(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundResumeAfterRestore(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundMasterVolume(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundUpdate(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundFade(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundGetPolyphony(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundUpdateCues(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSendMidi(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundReverb(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetHold(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundDummy(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundGetAudioCapability(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSuspend(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetVolume(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetPriority(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetLoop(EngineState *s, int argc, reg_t *argv);
+
+reg_t kGraphGetColorCount(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphRestoreBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphFillBoxBackground(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphFillBoxForeground(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphUpdateBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphRedrawBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv);
+
+reg_t kPalVaryInit(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryReverse(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryGetCurrentStep(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryDeinit(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryChangeTarget(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryChangeTicks(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryPauseResume(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv);
+
+reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteUnsetFlag(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSetIntensity(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteFindColor(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv);
+
+reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIORename(EngineState *s, int argc, reg_t *argv);
+#ifdef ENABLE_SCI32
+reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv);
#endif
//@}
diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp
deleted file mode 100644
index 465e0e92df..0000000000
--- a/engines/sci/engine/kernel32.cpp
+++ /dev/null
@@ -1,790 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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 ENABLE_SCI32
-
-#include "sci/engine/features.h"
-#include "sci/engine/kernel.h"
-#include "sci/engine/segment.h"
-#include "sci/engine/state.h"
-#include "sci/engine/selector.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
-#include "sci/graphics/frameout.h"
-
-#include "common/system.h"
-
-namespace Sci {
-
-// NOTE: 0x72-0x79, 0x85-0x86, 0x88 are from the GK2 demo (which has debug support) and are
-// just Dummy in other SCI2 games.
-static const char *sci2_default_knames[] = {
- /*0x00*/ "Load",
- /*0x01*/ "UnLoad",
- /*0x02*/ "ScriptID",
- /*0x03*/ "DisposeScript",
- /*0x04*/ "Lock",
- /*0x05*/ "ResCheck",
- /*0x06*/ "Purge",
- /*0x07*/ "Clone",
- /*0x08*/ "DisposeClone",
- /*0x09*/ "RespondsTo",
- /*0x0a*/ "SetNowSeen",
- /*0x0b*/ "NumLoops",
- /*0x0c*/ "NumCels",
- /*0x0d*/ "CelWide",
- /*0x0e*/ "CelHigh",
- /*0x0f*/ "GetHighPlanePri",
- /*0x10*/ "GetHighItemPri",
- /*0x11*/ "ShakeScreen",
- /*0x12*/ "OnMe",
- /*0x13*/ "ShowMovie",
- /*0x14*/ "SetVideoMode",
- /*0x15*/ "AddScreenItem",
- /*0x16*/ "DeleteScreenItem",
- /*0x17*/ "UpdateScreenItem",
- /*0x18*/ "FrameOut",
- /*0x19*/ "AddPlane",
- /*0x1a*/ "DeletePlane",
- /*0x1b*/ "UpdatePlane",
- /*0x1c*/ "RepaintPlane",
- /*0x1d*/ "SetShowStyle",
- /*0x1e*/ "ShowStylePercent",
- /*0x1f*/ "SetScroll",
- /*0x20*/ "AddMagnify",
- /*0x21*/ "DeleteMagnify",
- /*0x22*/ "IsHiRes",
- /*0x23*/ "Graph",
- /*0x24*/ "InvertRect",
- /*0x25*/ "TextSize",
- /*0x26*/ "Message",
- /*0x27*/ "TextColors",
- /*0x28*/ "TextFonts",
- /*0x29*/ "Dummy",
- /*0x2a*/ "SetQuitStr",
- /*0x2b*/ "EditText",
- /*0x2c*/ "InputText",
- /*0x2d*/ "CreateTextBitmap",
- /*0x2e*/ "DisposeTextBitmap",
- /*0x2f*/ "GetEvent",
- /*0x30*/ "GlobalToLocal",
- /*0x31*/ "LocalToGlobal",
- /*0x32*/ "MapKeyToDir",
- /*0x33*/ "HaveMouse",
- /*0x34*/ "SetCursor",
- /*0x35*/ "VibrateMouse",
- /*0x36*/ "SaveGame",
- /*0x37*/ "RestoreGame",
- /*0x38*/ "RestartGame",
- /*0x39*/ "GameIsRestarting",
- /*0x3a*/ "MakeSaveCatName",
- /*0x3b*/ "MakeSaveFileName",
- /*0x3c*/ "GetSaveFiles",
- /*0x3d*/ "GetSaveDir",
- /*0x3e*/ "CheckSaveGame",
- /*0x3f*/ "CheckFreeSpace",
- /*0x40*/ "DoSound",
- /*0x41*/ "DoAudio",
- /*0x42*/ "DoSync",
- /*0x43*/ "NewList",
- /*0x44*/ "DisposeList",
- /*0x45*/ "NewNode",
- /*0x46*/ "FirstNode",
- /*0x47*/ "LastNode",
- /*0x48*/ "EmptyList",
- /*0x49*/ "NextNode",
- /*0x4a*/ "PrevNode",
- /*0x4b*/ "NodeValue",
- /*0x4c*/ "AddAfter",
- /*0x4d*/ "AddToFront",
- /*0x4e*/ "AddToEnd",
- /*0x4f*/ "Dummy",
- /*0x50*/ "Dummy",
- /*0x51*/ "FindKey",
- /*0x52*/ "Dummy",
- /*0x53*/ "Dummy",
- /*0x54*/ "Dummy",
- /*0x55*/ "DeleteKey",
- /*0x56*/ "Dummy",
- /*0x57*/ "Dummy",
- /*0x58*/ "ListAt",
- /*0x59*/ "ListIndexOf",
- /*0x5a*/ "ListEachElementDo",
- /*0x5b*/ "ListFirstTrue",
- /*0x5c*/ "ListAllTrue",
- /*0x5d*/ "Random",
- /*0x5e*/ "Abs",
- /*0x5f*/ "Sqrt",
- /*0x60*/ "GetAngle",
- /*0x61*/ "GetDistance",
- /*0x62*/ "ATan",
- /*0x63*/ "SinMult",
- /*0x64*/ "CosMult",
- /*0x65*/ "SinDiv",
- /*0x66*/ "CosDiv",
- /*0x67*/ "GetTime",
- /*0x68*/ "Platform",
- /*0x69*/ "BaseSetter",
- /*0x6a*/ "DirLoop",
- /*0x6b*/ "CantBeHere",
- /*0x6c*/ "InitBresen",
- /*0x6d*/ "DoBresen",
- /*0x6e*/ "SetJump",
- /*0x6f*/ "AvoidPath",
- /*0x70*/ "InPolygon",
- /*0x71*/ "MergePoly",
- /*0x72*/ "SetDebug",
- /*0x73*/ "InspectObject",
- /*0x74*/ "MemoryInfo",
- /*0x75*/ "Profiler",
- /*0x76*/ "Record",
- /*0x77*/ "PlayBack",
- /*0x78*/ "MonoOut",
- /*0x79*/ "SetFatalStr",
- /*0x7a*/ "GetCWD",
- /*0x7b*/ "ValidPath",
- /*0x7c*/ "FileIO",
- /*0x7d*/ "Dummy",
- /*0x7e*/ "DeviceInfo",
- /*0x7f*/ "Palette",
- /*0x80*/ "PalVary",
- /*0x81*/ "PalCycle",
- /*0x82*/ "Array",
- /*0x83*/ "String",
- /*0x84*/ "RemapColors",
- /*0x85*/ "IntegrityChecking",
- /*0x86*/ "CheckIntegrity",
- /*0x87*/ "ObjectIntersect",
- /*0x88*/ "MarkMemory",
- /*0x89*/ "TextWidth",
- /*0x8a*/ "PointSize",
-
- // GK2 Demo only kernel functions
- /*0x8b*/ "AddLine",
- /*0x8c*/ "DeleteLine",
- /*0x8d*/ "UpdateLine",
- /*0x8e*/ "AddPolygon",
- /*0x8f*/ "DeletePolygon",
- /*0x90*/ "UpdatePolygon",
- /*0x91*/ "Bitmap",
- /*0x92*/ "ScrollWindow",
- /*0x93*/ "SetFontRes",
- /*0x94*/ "MovePlaneItems",
- /*0x95*/ "PreloadResource",
- /*0x96*/ "Dummy",
- /*0x97*/ "ResourceTrack",
- /*0x98*/ "CheckCDisc",
- /*0x99*/ "GetSaveCDisc",
- /*0x9a*/ "TestPoly",
- /*0x9b*/ "WinHelp",
- /*0x9c*/ "LoadChunk",
- /*0x9d*/ "SetPalStyleRange",
- /*0x9e*/ "AddPicAt",
- /*0x9f*/ "MessageBox"
-};
-
-static const char *sci21_default_knames[] = {
- /*0x00*/ "Load",
- /*0x01*/ "UnLoad",
- /*0x02*/ "ScriptID",
- /*0x03*/ "DisposeScript",
- /*0x04*/ "Lock",
- /*0x05*/ "ResCheck",
- /*0x06*/ "Purge",
- /*0x07*/ "SetLanguage",
- /*0x08*/ "Dummy",
- /*0x09*/ "Dummy",
- /*0x0a*/ "Clone",
- /*0x0b*/ "DisposeClone",
- /*0x0c*/ "RespondsTo",
- /*0x0d*/ "FindSelector",
- /*0x0e*/ "FindClass",
- /*0x0f*/ "Dummy",
- /*0x10*/ "Dummy",
- /*0x11*/ "Dummy",
- /*0x12*/ "Dummy",
- /*0x13*/ "Dummy",
- /*0x14*/ "SetNowSeen",
- /*0x15*/ "NumLoops",
- /*0x16*/ "NumCels",
- /*0x17*/ "IsOnMe",
- /*0x18*/ "AddMagnify",
- /*0x19*/ "DeleteMagnify",
- /*0x1a*/ "CelRect",
- /*0x1b*/ "BaseLineSpan",
- /*0x1c*/ "CelWide",
- /*0x1d*/ "CelHigh",
- /*0x1e*/ "AddScreenItem",
- /*0x1f*/ "DeleteScreenItem",
- /*0x20*/ "UpdateScreenItem",
- /*0x21*/ "FrameOut",
- /*0x22*/ "CelInfo",
- /*0x23*/ "Bitmap",
- /*0x24*/ "CelLink",
- /*0x25*/ "Dummy",
- /*0x26*/ "Dummy",
- /*0x27*/ "Dummy",
- /*0x28*/ "AddPlane",
- /*0x29*/ "DeletePlane",
- /*0x2a*/ "UpdatePlane",
- /*0x2b*/ "RepaintPlane",
- /*0x2c*/ "GetHighPlanePri",
- /*0x2d*/ "GetHighItemPri",
- /*0x2e*/ "SetShowStyle",
- /*0x2f*/ "ShowStylePercent",
- /*0x30*/ "SetScroll",
- /*0x31*/ "MovePlaneItems",
- /*0x32*/ "ShakeScreen",
- /*0x33*/ "Dummy",
- /*0x34*/ "Dummy",
- /*0x35*/ "Dummy",
- /*0x36*/ "Dummy",
- /*0x37*/ "IsHiRes",
- /*0x38*/ "SetVideoMode",
- /*0x39*/ "ShowMovie",
- /*0x3a*/ "Robot",
- /*0x3b*/ "CreateTextBitmap",
- /*0x3c*/ "Random",
- /*0x3d*/ "Abs",
- /*0x3e*/ "Sqrt",
- /*0x3f*/ "GetAngle",
- /*0x40*/ "GetDistance",
- /*0x41*/ "ATan",
- /*0x42*/ "SinMult",
- /*0x43*/ "CosMult",
- /*0x44*/ "SinDiv",
- /*0x45*/ "CosDiv",
- /*0x46*/ "Text",
- /*0x47*/ "Dummy",
- /*0x48*/ "Message",
- /*0x49*/ "Font",
- /*0x4a*/ "EditText",
- /*0x4b*/ "InputText",
- /*0x4c*/ "ScrollWindow",
- /*0x4d*/ "Dummy",
- /*0x4e*/ "Dummy",
- /*0x4f*/ "Dummy",
- /*0x50*/ "GetEvent",
- /*0x51*/ "GlobalToLocal",
- /*0x52*/ "LocalToGlobal",
- /*0x53*/ "MapKeyToDir",
- /*0x54*/ "HaveMouse",
- /*0x55*/ "SetCursor",
- /*0x56*/ "VibrateMouse", // NOTE: Not in SCI3, instead replaced by Dummy.
- /*0x57*/ "Dummy",
- /*0x58*/ "Dummy",
- /*0x59*/ "Dummy",
- /*0x5a*/ "List",
- /*0x5b*/ "Array",
- /*0x5c*/ "String",
- /*0x5d*/ "FileIO",
- /*0x5e*/ "BaseSetter",
- /*0x5f*/ "DirLoop",
- /*0x60*/ "CantBeHere",
- /*0x61*/ "InitBresen",
- /*0x62*/ "DoBresen",
- /*0x63*/ "SetJump",
- /*0x64*/ "AvoidPath",
- /*0x65*/ "InPolygon",
- /*0x66*/ "MergePoly",
- /*0x67*/ "ObjectIntersect",
- /*0x68*/ "Dummy",
- /*0x69*/ "MemoryInfo",
- /*0x6a*/ "DeviceInfo",
- /*0x6b*/ "Palette",
- /*0x6c*/ "PalVary",
- /*0x6d*/ "PalCycle",
- /*0x6e*/ "RemapColors",
- /*0x6f*/ "AddLine",
- /*0x70*/ "DeleteLine",
- /*0x71*/ "UpdateLine",
- /*0x72*/ "AddPolygon",
- /*0x73*/ "DeletePolygon",
- /*0x74*/ "UpdatePolygon",
- /*0x75*/ "DoSound",
- /*0x76*/ "DoAudio",
- /*0x77*/ "DoSync",
- /*0x78*/ "Save",
- /*0x79*/ "GetTime",
- /*0x7a*/ "Platform",
- /*0x7b*/ "CD",
- /*0x7c*/ "SetQuitStr",
- /*0x7d*/ "GetConfig",
- /*0x7e*/ "Table",
- /*0x7f*/ "WinHelp", // Windows only
- /*0x80*/ "Dummy",
- /*0x81*/ "Dummy",
- /*0x82*/ "Dummy",
- /*0x83*/ "Dummy",
- /*0x84*/ "Dummy",
- /*0x85*/ "Dummy",
- /*0x86*/ "Dummy",
- /*0x87*/ "Dummy",
- /*0x88*/ "Dummy",
- /*0x89*/ "Dummy",
- /*0x8a*/ "LoadChunk",
- /*0x8b*/ "SetPalStyleRange",
- /*0x8c*/ "AddPicAt",
- /*0x8d*/ "Dummy",
- /*0x8e*/ "NewRoom",
- /*0x8f*/ "Dummy",
- /*0x90*/ "Priority",
- /*0x91*/ "MorphOn",
- /*0x92*/ "PlayVMD",
- /*0x93*/ "SetHotRectangles",
- /*0x94*/ "MulDiv",
- /*0x95*/ "GetSierraProfileInt", // Windows only
- /*0x96*/ "GetSierraProfileString", // Windows only
- /*0x97*/ "SetWindowsOption", // Windows only
- /*0x98*/ "GetWindowsOption", // Windows only
- /*0x99*/ "WinDLL", // Windows only
-
- // SCI3
- /*0x9a*/ "Dummy",
- /*0x9b*/ "Dummy",
- /*0x9c*/ "DeletePic"
-};
-
-enum {
- kKernelEntriesSci2 = 0x8b,
- kKernelEntriesGk2Demo = 0xa0,
- kKernelEntriesSci21 = 0x9a,
- kKernelEntriesSci3 = 0x9d
-};
-
-void Kernel::setKernelNamesSci2() {
- _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
-}
-
-void Kernel::setKernelNamesSci21() {
- // Some SCI games use a modified SCI2 kernel table instead of the SCI2.1/SCI3 kernel table.
- // The GK2 demo does this as well as at least one version of KQ7. We detect which version
- // to use based on where kDoSound is called from Sound::play().
-
- // This is interesting because they all have the same interpreter version (2.100.002), yet
- // they would not be compatible with other games of the same interpreter.
-
- if (g_sci->_features->detectSci21KernelType() == SCI_VERSION_2) {
- _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
- // OnMe is IsOnMe here, but they should be compatible
- _kernelNames[0x23] = "Robot"; // Graph in SCI2
- _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
- } else {
- // TODO: Differentiate between SCI2.1/3
- _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3);
- }
-}
-
-// SCI2 Kernel Functions
-
-reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
- // Returns 0 if the screen width or height is less than 640 or 400, respectively.
- if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
- return make_reg(0, 0);
-
- return make_reg(0, 1);
-}
-
-reg_t kArray(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0: { // New
- reg_t arrayHandle;
- SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle);
- array->setType(argv[2].toUint16());
- array->setSize(argv[1].toUint16());
- return arrayHandle;
- }
- case 1: { // Size
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- return make_reg(0, array->getSize());
- }
- case 2: { // At (return value at an index)
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- return array->getValue(argv[2].toUint16());
- }
- case 3: { // Atput (put value at an index)
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
-
- uint32 index = argv[2].toUint16();
- uint32 count = argc - 3;
-
- if (index + count > 65535)
- break;
-
- if (array->getSize() < index + count)
- array->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- array->setValue(i + index, argv[i + 3]);
-
- return argv[1]; // We also have to return the handle
- }
- case 4: // Free
- // Freeing of arrays is handled by the garbage collector
- return s->r_acc;
- case 5: { // Fill
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- uint16 index = argv[2].toUint16();
-
- // A count of -1 means fill the rest of the array
- uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16();
- uint16 arraySize = array->getSize();
-
- if (arraySize < index + count)
- array->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- array->setValue(i + index, argv[4]);
-
- return argv[1];
- }
- case 6: { // Cpy
- SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
- SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
- uint32 index1 = argv[2].toUint16();
- uint32 index2 = argv[4].toUint16();
-
- // The original engine ignores bad copies too
- if (index2 > array2->getSize())
- break;
-
- // A count of -1 means fill the rest of the array
- uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16();
-
- if (array1->getSize() < index1 + count)
- array1->setSize(index1 + count);
-
- for (uint16 i = 0; i < count; i++)
- array1->setValue(i + index1, array2->getValue(i + index2));
-
- return argv[1];
- }
- case 7: // Cmp
- // Not implemented in SSCI
- return s->r_acc;
- case 8: { // Dup
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- reg_t arrayHandle;
- SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
-
- dupArray->setType(array->getType());
- dupArray->setSize(array->getSize());
-
- for (uint32 i = 0; i < array->getSize(); i++)
- dupArray->setValue(i, array->getValue(i));
-
- return arrayHandle;
- }
- case 9: // Getdata
- if (!s->_segMan->isHeapObject(argv[1]))
- return argv[1];
-
- return GET_SEL32(s->_segMan, argv[1], SELECTOR(data));
- default:
- error("Unknown kArray subop %d", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-reg_t kString(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0: { // New
- reg_t stringHandle;
- SciString *string = s->_segMan->allocateString(&stringHandle);
- string->setSize(argv[1].toUint16());
-
- // Make sure the first character is a null character
- if (string->getSize() > 0)
- string->setValue(0, 0);
-
- return stringHandle;
- }
- case 1: // Size
- return make_reg(0, s->_segMan->getString(argv[1]).size());
- case 2: // At (return value at an index)
- return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]);
- case 3: { // Atput (put value at an index)
- SciString *string = s->_segMan->lookupString(argv[1]);
-
- uint32 index = argv[2].toUint16();
- uint32 count = argc - 3;
-
- if (index + count > 65535)
- break;
-
- if (string->getSize() < index + count)
- string->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- string->setValue(i + index, argv[i + 3].toUint16());
-
- return argv[1]; // We also have to return the handle
- }
- case 4: // Free
- // Freeing of strings is handled by the garbage collector
- return s->r_acc;
- case 5: { // Fill
- SciString *string = s->_segMan->lookupString(argv[1]);
- uint16 index = argv[2].toUint16();
-
- // A count of -1 means fill the rest of the array
- uint16 count = argv[3].toSint16() == -1 ? string->getSize() - index : argv[3].toUint16();
- uint16 stringSize = string->getSize();
-
- if (stringSize < index + count)
- string->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- string->setValue(i + index, argv[4].toUint16());
-
- return argv[1];
- }
- case 6: { // Cpy
- Common::String string2 = s->_segMan->getString(argv[3]);
- uint32 index1 = argv[2].toUint16();
- uint32 index2 = argv[4].toUint16();
-
- // The original engine ignores bad copies too
- if (index2 > string2.size())
- break;
-
- // A count of -1 means fill the rest of the array
- uint32 count = argv[5].toSint16() == -1 ? string2.size() - index2 + 1 : argv[5].toUint16();
-
- // We have a special case here for argv[1] being a system string
- if (argv[1].segment == s->sys_strings_segment) {
- // Resize if necessary
- if ((uint32)s->sys_strings->_strings[argv[1].toUint16()]._maxSize < index1 + count) {
- delete[] s->sys_strings->_strings[argv[1].toUint16()]._value;
- s->sys_strings->_strings[argv[1].toUint16()]._maxSize = index1 + count;
- s->sys_strings->_strings[argv[1].toUint16()]._value = new char[index1 + count];
- memset(s->sys_strings->_strings[argv[1].toUint16()]._value, 0, index1 + count);
- }
-
- strncpy(s->sys_strings->_strings[argv[1].toUint16()]._value + index1, string2.c_str() + index2, count);
- } else {
- SciString *string1 = s->_segMan->lookupString(argv[1]);
-
- if (string1->getSize() < index1 + count)
- string1->setSize(index1 + count);
-
- // Note: We're accessing from c_str() here because the string's size ignores
- // the trailing 0 and therefore triggers an assert when doing string2[i + index2].
- for (uint16 i = 0; i < count; i++)
- string1->setValue(i + index1, string2.c_str()[i + index2]);
- }
-
- } return argv[1];
- case 7: { // Cmp
- Common::String string1 = argv[1].isNull() ? "" : s->_segMan->getString(argv[1]);
- Common::String string2 = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
-
- if (argc == 4) // Strncmp
- return make_reg(0, strncmp(string1.c_str(), string2.c_str(), argv[3].toUint16()));
- else // Strcmp
- return make_reg(0, strcmp(string1.c_str(), string2.c_str()));
- }
- case 8: { // Dup
- Common::String string = s->_segMan->getString(argv[1]);
- reg_t stringHandle;
- SciString *dupString = s->_segMan->allocateString(&stringHandle);
- dupString->setSize(string.size() + 1);
-
- for (uint32 i = 0; i < string.size(); i++)
- dupString->setValue(i, string.c_str()[i]);
-
- dupString->setValue(dupString->getSize() - 1, 0);
-
- return stringHandle;
- }
- case 9: // Getdata
- if (!s->_segMan->isHeapObject(argv[1]))
- return argv[1];
-
- return GET_SEL32(s->_segMan, argv[1], SELECTOR(data));
- case 10: // Stringlen
- return make_reg(0, s->_segMan->strlen(argv[1]));
- case 11: { // Printf
- reg_t stringHandle;
- s->_segMan->allocateString(&stringHandle);
-
- reg_t *adjustedArgs = new reg_t[argc];
- adjustedArgs[0] = stringHandle;
- memcpy(&adjustedArgs[1], argv + 1, (argc - 1) * sizeof(reg_t));
-
- kFormat(s, argc, adjustedArgs);
- delete[] adjustedArgs;
- return stringHandle;
- }
- case 12: // Printf Buf
- return kFormat(s, argc - 1, argv + 1);
- case 13: { // atoi
- Common::String string = s->_segMan->getString(argv[1]);
- return make_reg(0, (uint16)atoi(string.c_str()));
- }
- default:
- error("Unknown kString subop %d", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-reg_t kSave(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 2: // GetSaveDir
- // Yay! Reusing the old kernel function!
- return kGetSaveDir(s, argc - 1, argv + 1);
- case 8:
- // TODO
- // This function has to return something other than 0 to proceed
- return s->r_acc;
- default:
- warning("Unknown/unhandled kSave subop %d", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
- reg_t viewObj = argv[0];
-
- g_sci->_gfxFrameout->kernelAddScreenItem(viewObj);
- return NULL_REG;
-}
-
-reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
- //reg_t viewObj = argv[0];
-
- //warning("kUpdateScreenItem, object %04x:%04x, view %d, loop %d, cel %d, pri %d", PRINT_REG(viewObj), viewId, loopNo, celNo, priority);
- return NULL_REG;
-}
-
-reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
- reg_t viewObj = argv[0];
-
- g_sci->_gfxFrameout->kernelDeleteScreenItem(viewObj);
-
- /*
- reg_t viewObj = argv[0];
- uint16 viewId = GET_SEL32V(s->_segMan, viewObj, SELECTOR(view));
- int16 loopNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(loop));
- int16 celNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(cel));
- //int16 leftPos = 0;
- //int16 topPos = 0;
- int16 priority = GET_SEL32V(s->_segMan, viewObj, SELECTOR(priority));
- //int16 control = 0;
- */
-
- // TODO
-
- //warning("kDeleteScreenItem, view %d, loop %d, cel %d, pri %d", viewId, loopNo, celNo, priority);
- return NULL_REG;
-}
-
-reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelAddPlane(planeObj);
- warning("kAddPlane object %04x:%04x", PRINT_REG(planeObj));
- return NULL_REG;
-}
-
-reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelDeletePlane(planeObj);
- warning("kDeletePlane object %04x:%04x", PRINT_REG(planeObj));
- return NULL_REG;
-}
-
-reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelUpdatePlane(planeObj);
- return s->r_acc;
-}
-
-reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) {
- reg_t picObj = argv[0];
-
- // TODO
-
- warning("kRepaintPlane object %04x:%04x", PRINT_REG(picObj));
- return NULL_REG;
-}
-
-reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
- warning("kGetHighPlanePri: %d", g_sci->_gfxFrameout->kernelGetHighPlanePri());
- return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri());
-}
-
-reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
- // This kernel call likely seems to be doing the screen updates,
- // as its called right after a view is updated
-
- // TODO
- g_sci->_gfxFrameout->kernelFrameout();
-
- return NULL_REG;
-}
-
-reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
- // Tests if the cursor is on the passed object
-
- uint16 x = argv[0].toUint16();
- uint16 y = argv[1].toUint16();
- reg_t targetObject = argv[2];
- // TODO: argv[3] - it's usually 0
- Common::Rect nsRect;
-
- // Get the bounding rectangle of the object
- nsRect.left = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsLeft));
- nsRect.top = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsTop));
- nsRect.right = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsRight));
- nsRect.bottom = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsBottom));
-
- //warning("kOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16());
-
- return make_reg(0, nsRect.contains(x, y));
-}
-
-reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) {
- // kAvoidPath already implements this
- return kAvoidPath(s, argc, argv);
-}
-
-reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
- // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1
- reg_t object = argv[3];
- Common::String text = s->_segMan->getString(GET_SEL32(s->_segMan, object, SELECTOR(text)));
- debug("kCreateTextBitmap: %s", text.c_str());
-
- return NULL_REG;
-}
-
-} // End of namespace Sci
-
-#endif // ENABLE_SCI32
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
new file mode 100644
index 0000000000..b2b8eb593e
--- /dev/null
+++ b/engines/sci/engine/kernel_tables.h
@@ -0,0 +1,1031 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 SCI_ENGINE_KERNEL_TABLES_H
+#define SCI_ENGINE_KERNEL_TABLES_H
+
+#include "sci/engine/workarounds.h"
+
+namespace Sci {
+
+// [io] -> either integer or object
+// (io) -> optionally integer AND an object
+// (i) -> optional integer
+// . -> any type
+// i* -> optional multiple integers
+// .* -> any parameters afterwards (or none)
+
+struct SciKernelMapSubEntry {
+ SciVersion fromVersion;
+ SciVersion toVersion;
+
+ uint16 id;
+
+ const char *name;
+ KernelFunctionCall *function;
+
+ const char *signature;
+ const SciWorkaroundEntry *workarounds;
+};
+
+#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL }
+
+
+#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
+#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
+#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE
+#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
+#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
+#define SIG_SCI21 SCI_VERSION_2_1, SCI_VERSION_2_1
+
+#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1
+#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE
+
+// SCI-Sound-Version
+#define SIG_SOUNDSCI0 SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE
+#define SIG_SOUNDSCI1EARLY SCI_VERSION_1_EARLY, SCI_VERSION_1_EARLY
+#define SIG_SOUNDSCI1LATE SCI_VERSION_1_LATE, SCI_VERSION_1_LATE
+#define SIG_SOUNDSCI21 SCI_VERSION_2_1, SCI_VERSION_2_1
+
+#define SIGFOR_ALL 0x3f
+#define SIGFOR_DOS 1 << 0
+#define SIGFOR_PC98 1 << 1
+#define SIGFOR_WIN 1 << 2
+#define SIGFOR_MAC 1 << 3
+#define SIGFOR_AMIGA 1 << 4
+#define SIGFOR_ATARI 1 << 5
+#define SIGFOR_PC SIGFOR_DOS|SIGFOR_WIN
+
+#define SIG_EVERYWHERE SIG_SCIALL, SIGFOR_ALL
+
+#define MAP_CALL(_name_) #_name_, k##_name_
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kDoSound_subops[] = {
+ { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL },
+ { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL },
+ { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundRestore), "(o)", NULL },
+ { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL },
+ { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL },
+ { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL },
+ { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
+ { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL },
+ { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
+ { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
+ { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
+ { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL },
+ { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
+ // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and
+ // playBed. The methods are the same, apart from the second integer parameter: it's 0 in
+ // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what
+ // it actually does internally
+ { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
+ { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
+ { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL },
+ { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "i", NULL },
+ { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
+ { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL },
+ // ^^ Longbow demo
+ { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundRestore), "", NULL },
+ { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
+ { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
+ { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), "oiii(i)", NULL },
+ { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+#ifdef ENABLE_SCI32
+ { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI21, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
+ { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), NULL, NULL },
+ { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), NULL, NULL },
+ { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o(i)", NULL },
+ // ^^ TODO: if this is really the only change between SCI1LATE AND SCI21, we could rename the
+ // SIG_SOUNDSCI1LATE #define to SIG_SINCE_SOUNDSCI1LATE and make it being SCI1LATE+. Although
+ // I guess there are many more changes somewhere
+ // TODO: Quest for Glory 4 (SCI2.1) uses the old scheme, we need to detect it accordingly
+ // signature for SCI21 should be "o"
+ { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL },
+ { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, NULL },
+ { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
+ { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL },
+ { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), NULL, NULL },
+ { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), NULL, NULL },
+ { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL },
+ { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
+ { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+#endif
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kGraph_subops[] = {
+ { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start
+ { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
+ // 3 - set palette via resource
+ { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", kGraphDrawLine_workarounds },
+ // 5 - nop
+ // 6 - draw pattern
+ { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", kGraphSaveBox_workarounds },
+ { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
+ // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
+ { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
+ { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
+ { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", kGraphUpdateBox_workarounds }, // kq6 hires
+ { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", kGraphUpdateBox_workarounds },
+ { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds },
+ { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
+ { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kPalVary_subops[] = {
+ { SIG_SCI21, 0, MAP_CALL(PalVaryInit), "ii(i)(i)(i)", NULL },
+ { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
+ { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kPalette_subops[] = {
+ { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
+ { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "[r0]", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+static const SciKernelMapSubEntry kFileIO_subops[] = {
+ { SIG_SCI32, 0, MAP_CALL(FileIOOpen), "r(i)", NULL },
+ { SIG_SCIALL, 0, MAP_CALL(FileIOOpen), "ri", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(FileIOClose), "i", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(FileIOReadRaw), "iri", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(FileIOWriteRaw), "iri", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(FileIOUnlink), "r", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(FileIOReadString), "rii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(FileIOWriteString), "ir", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(FileIOSeek), "iii", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(FileIOFindFirst), "rri", NULL },
+ { SIG_SCIALL, 9, MAP_CALL(FileIOFindNext), "r", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(FileIOExists), "r", NULL },
+ { SIG_SINCE_SCI11, 11, MAP_CALL(FileIORename), "rr", NULL },
+#ifdef ENABLE_SCI32
+ { SIG_SCI32, 13, MAP_CALL(FileIOReadByte), "i", NULL },
+ { SIG_SCI32, 14, MAP_CALL(FileIOWriteByte), "ii", NULL },
+ { SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL },
+ { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
+ { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo
+#endif
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+#ifdef ENABLE_SCI32
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kList_subops[] = {
+ { SIG_SCI21, 0, MAP_CALL(NewList), "", NULL },
+ { SIG_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
+ { SIG_SCI21, 2, MAP_CALL(NewNode), ".", NULL },
+ { SIG_SCI21, 3, MAP_CALL(FirstNode), "[l0]", NULL },
+ { SIG_SCI21, 4, MAP_CALL(LastNode), "l", NULL },
+ { SIG_SCI21, 5, MAP_CALL(EmptyList), "l", NULL },
+ { SIG_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
+ { SIG_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
+ { SIG_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
+ { SIG_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
+ { SIG_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
+ { SIG_SCI21, 11, MAP_CALL(AddToEnd), "ln.", NULL },
+ { SIG_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
+ { SIG_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
+ { SIG_SCI21, 14, MAP_CALL(MoveToEnd), "ln", NULL },
+ { SIG_SCI21, 15, MAP_CALL(FindKey), "l.", NULL },
+ { SIG_SCI21, 16, MAP_CALL(DeleteKey), "l.", NULL },
+ { SIG_SCI21, 17, MAP_CALL(ListAt), "li", NULL },
+ // FIXME: This doesn't seem to be ListIndexOf. In Torin demo, an index is
+ // passed as a second parameter instead of an object. Thus, it seems to
+ // be something like ListAt instead... If we swap the two subops though,
+ // Torin demo crashes complaining that it tried to send to a non-object,
+ // therefore the semantics might be different here (signature was l[o0])
+ // In SQ6 object is passed right when skipping the intro
+ { SIG_SCI21, 18, MAP_CALL(StubNull), "l[io]", NULL },
+ { SIG_SCI21, 19, MAP_CALL(ListEachElementDo), "li(.*)", NULL },
+ { SIG_SCI21, 20, MAP_CALL(ListFirstTrue), "li(.*)", NULL },
+ { SIG_SCI21, 21, MAP_CALL(ListAllTrue), "li(.*)", NULL },
+ { SIG_SCI21, 22, MAP_CALL(Sort), "ooo", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+#endif
+
+struct SciKernelMapEntry {
+ const char *name;
+ KernelFunctionCall *function;
+
+ SciVersion fromVersion;
+ SciVersion toVersion;
+ byte forPlatform;
+
+ const char *signature;
+ const SciKernelMapSubEntry *subFunctions;
+ const SciWorkaroundEntry *workarounds;
+};
+
+// name, version/platform, signature, sub-signatures, workarounds
+static SciKernelMapEntry s_kernelMap[] = {
+ { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
+ { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
+ { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
+ { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
+ { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
+ { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL },
+#endif
+ { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+ { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds },
+ { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds },
+ { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
+ { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
+ { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, kDeviceInfo_workarounds }, // subop
+ { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, NULL },
+ // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro
+ // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same
+ { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, NULL },
+ { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DisposeList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(DisposeScript), SIG_EVERYWHERE, "i(i*)", NULL, kDisposeScript_workarounds },
+ { MAP_CALL(DisposeWindow), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(DoAudio), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(DoAvoider), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DoSound), SIG_EVERYWHERE, "i(.*)", kDoSound_subops, NULL },
+ { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)([ri])", NULL, NULL }, // reference for kq6 hires
+ { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
+ { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
+ { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
+ { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
+ { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
+ { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL },
+ { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds },
+ { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
+ { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
+ { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds },
+ { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
+ { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
+ { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
+ { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
+ { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
+ { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
+ { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
+ { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
+ { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, kIsObject_workarounds },
+ { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
+ { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
+ { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
+ { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
+ { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, kNewWindow_workarounds },
+ { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
+ { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
+ { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
+ { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
+ { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL },
+ { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
+ { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
+ { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "rir", NULL, NULL },
+ { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(SaveGame), SIG_EVERYWHERE, "rir(r)", NULL, NULL },
+ { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
+ { MAP_CALL(SetCursor), SIG_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL },
+ // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why
+ { MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL },
+ { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
+ { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds },
+ { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
+ { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL },
+ { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, kStrAt_workarounds },
+ { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL },
+ { MAP_CALL(StrCpy), SIG_EVERYWHERE, "r[r0](i)", NULL, NULL },
+ { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
+ { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
+ { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
+ { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
+
+#ifdef ENABLE_SCI32
+ // SCI2 Kernel Functions
+ { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
+ { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
+ { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL },
+ { MAP_CALL(OnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
+ { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+
+ // SCI2.1 Kernel Functions
+ { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
+ { MAP_CALL(List), SIG_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
+ { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
+ { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
+#endif
+};
+
+/** Default kernel name table. */
+static const char *s_defaultKernelNames[] = {
+ /*0x00*/ "Load",
+ /*0x01*/ "UnLoad",
+ /*0x02*/ "ScriptID",
+ /*0x03*/ "DisposeScript",
+ /*0x04*/ "Clone",
+ /*0x05*/ "DisposeClone",
+ /*0x06*/ "IsObject",
+ /*0x07*/ "RespondsTo",
+ /*0x08*/ "DrawPic",
+ /*0x09*/ "Dummy", // Show
+ /*0x0a*/ "PicNotValid",
+ /*0x0b*/ "Animate",
+ /*0x0c*/ "SetNowSeen",
+ /*0x0d*/ "NumLoops",
+ /*0x0e*/ "NumCels",
+ /*0x0f*/ "CelWide",
+ /*0x10*/ "CelHigh",
+ /*0x11*/ "DrawCel",
+ /*0x12*/ "AddToPic",
+ /*0x13*/ "NewWindow",
+ /*0x14*/ "GetPort",
+ /*0x15*/ "SetPort",
+ /*0x16*/ "DisposeWindow",
+ /*0x17*/ "DrawControl",
+ /*0x18*/ "HiliteControl",
+ /*0x19*/ "EditControl",
+ /*0x1a*/ "TextSize",
+ /*0x1b*/ "Display",
+ /*0x1c*/ "GetEvent",
+ /*0x1d*/ "GlobalToLocal",
+ /*0x1e*/ "LocalToGlobal",
+ /*0x1f*/ "MapKeyToDir",
+ /*0x20*/ "DrawMenuBar",
+ /*0x21*/ "MenuSelect",
+ /*0x22*/ "AddMenu",
+ /*0x23*/ "DrawStatus",
+ /*0x24*/ "Parse",
+ /*0x25*/ "Said",
+ /*0x26*/ "SetSynonyms", // Portrait (KQ6 hires)
+ /*0x27*/ "HaveMouse",
+ /*0x28*/ "SetCursor",
+ // FOpen (SCI0)
+ // FPuts (SCI0)
+ // FGets (SCI0)
+ // FClose (SCI0)
+ /*0x29*/ "SaveGame",
+ /*0x2a*/ "RestoreGame",
+ /*0x2b*/ "RestartGame",
+ /*0x2c*/ "GameIsRestarting",
+ /*0x2d*/ "DoSound",
+ /*0x2e*/ "NewList",
+ /*0x2f*/ "DisposeList",
+ /*0x30*/ "NewNode",
+ /*0x31*/ "FirstNode",
+ /*0x32*/ "LastNode",
+ /*0x33*/ "EmptyList",
+ /*0x34*/ "NextNode",
+ /*0x35*/ "PrevNode",
+ /*0x36*/ "NodeValue",
+ /*0x37*/ "AddAfter",
+ /*0x38*/ "AddToFront",
+ /*0x39*/ "AddToEnd",
+ /*0x3a*/ "FindKey",
+ /*0x3b*/ "DeleteKey",
+ /*0x3c*/ "Random",
+ /*0x3d*/ "Abs",
+ /*0x3e*/ "Sqrt",
+ /*0x3f*/ "GetAngle",
+ /*0x40*/ "GetDistance",
+ /*0x41*/ "Wait",
+ /*0x42*/ "GetTime",
+ /*0x43*/ "StrEnd",
+ /*0x44*/ "StrCat",
+ /*0x45*/ "StrCmp",
+ /*0x46*/ "StrLen",
+ /*0x47*/ "StrCpy",
+ /*0x48*/ "Format",
+ /*0x49*/ "GetFarText",
+ /*0x4a*/ "ReadNumber",
+ /*0x4b*/ "BaseSetter",
+ /*0x4c*/ "DirLoop",
+ /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions
+ /*0x4e*/ "OnControl",
+ /*0x4f*/ "InitBresen",
+ /*0x50*/ "DoBresen",
+ /*0x51*/ "Platform", // DoAvoider (SCI0)
+ /*0x52*/ "SetJump",
+ /*0x53*/ "SetDebug",
+ /*0x54*/ "Dummy", // InspectObj
+ /*0x55*/ "Dummy", // ShowSends
+ /*0x56*/ "Dummy", // ShowObjs
+ /*0x57*/ "Dummy", // ShowFree
+ /*0x58*/ "MemoryInfo",
+ /*0x59*/ "Dummy", // StackUsage
+ /*0x5a*/ "Dummy", // Profiler
+ /*0x5b*/ "GetMenu",
+ /*0x5c*/ "SetMenu",
+ /*0x5d*/ "GetSaveFiles",
+ /*0x5e*/ "GetCWD",
+ /*0x5f*/ "CheckFreeSpace",
+ /*0x60*/ "ValidPath",
+ /*0x61*/ "CoordPri",
+ /*0x62*/ "StrAt",
+ /*0x63*/ "DeviceInfo",
+ /*0x64*/ "GetSaveDir",
+ /*0x65*/ "CheckSaveGame",
+ /*0x66*/ "ShakeScreen",
+ /*0x67*/ "FlushResources",
+ /*0x68*/ "SinMult",
+ /*0x69*/ "CosMult",
+ /*0x6a*/ "SinDiv",
+ /*0x6b*/ "CosDiv",
+ /*0x6c*/ "Graph",
+ /*0x6d*/ "Joystick",
+ // End of kernel function table for SCI0
+ /*0x6e*/ "Dummy", // ShiftScreen
+ /*0x6f*/ "Palette",
+ /*0x70*/ "MemorySegment",
+ /*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1)
+ /*0x72*/ "Memory",
+ /*0x73*/ "Dummy", // ListOps
+ /*0x74*/ "FileIO",
+ /*0x75*/ "DoAudio",
+ /*0x76*/ "DoSync",
+ /*0x77*/ "AvoidPath",
+ /*0x78*/ "Sort", // StrSplit (SCI01)
+ /*0x79*/ "Dummy", // ATan
+ /*0x7a*/ "Lock",
+ /*0x7b*/ "StrSplit",
+ /*0x7c*/ "GetMessage", // Message (SCI1.1)
+ /*0x7d*/ "IsItSkip",
+ /*0x7e*/ "MergePoly",
+ /*0x7f*/ "ResCheck",
+ /*0x80*/ "AssertPalette",
+ /*0x81*/ "TextColors",
+ /*0x82*/ "TextFonts",
+ /*0x83*/ "Dummy", // Record
+ /*0x84*/ "Dummy", // PlayBack
+ /*0x85*/ "ShowMovie",
+ /*0x86*/ "SetVideoMode",
+ /*0x87*/ "SetQuitStr",
+ /*0x88*/ "Dummy" // DbugStr
+};
+
+#ifdef ENABLE_SCI32
+
+// NOTE: 0x72-0x79, 0x85-0x86, 0x88 are from the GK2 demo (which has debug support) and are
+// just Dummy in other SCI2 games.
+static const char *sci2_default_knames[] = {
+ /*0x00*/ "Load",
+ /*0x01*/ "UnLoad",
+ /*0x02*/ "ScriptID",
+ /*0x03*/ "DisposeScript",
+ /*0x04*/ "Lock",
+ /*0x05*/ "ResCheck",
+ /*0x06*/ "Purge",
+ /*0x07*/ "Clone",
+ /*0x08*/ "DisposeClone",
+ /*0x09*/ "RespondsTo",
+ /*0x0a*/ "SetNowSeen",
+ /*0x0b*/ "NumLoops",
+ /*0x0c*/ "NumCels",
+ /*0x0d*/ "CelWide",
+ /*0x0e*/ "CelHigh",
+ /*0x0f*/ "GetHighPlanePri",
+ /*0x10*/ "GetHighItemPri",
+ /*0x11*/ "ShakeScreen",
+ /*0x12*/ "OnMe",
+ /*0x13*/ "ShowMovie",
+ /*0x14*/ "SetVideoMode",
+ /*0x15*/ "AddScreenItem",
+ /*0x16*/ "DeleteScreenItem",
+ /*0x17*/ "UpdateScreenItem",
+ /*0x18*/ "FrameOut",
+ /*0x19*/ "AddPlane",
+ /*0x1a*/ "DeletePlane",
+ /*0x1b*/ "UpdatePlane",
+ /*0x1c*/ "RepaintPlane",
+ /*0x1d*/ "SetShowStyle",
+ /*0x1e*/ "ShowStylePercent",
+ /*0x1f*/ "SetScroll",
+ /*0x20*/ "AddMagnify",
+ /*0x21*/ "DeleteMagnify",
+ /*0x22*/ "IsHiRes",
+ /*0x23*/ "Graph",
+ /*0x24*/ "InvertRect",
+ /*0x25*/ "TextSize",
+ /*0x26*/ "Message",
+ /*0x27*/ "TextColors",
+ /*0x28*/ "TextFonts",
+ /*0x29*/ "Dummy",
+ /*0x2a*/ "SetQuitStr",
+ /*0x2b*/ "EditText",
+ /*0x2c*/ "InputText",
+ /*0x2d*/ "CreateTextBitmap",
+ /*0x2e*/ "DisposeTextBitmap",
+ /*0x2f*/ "GetEvent",
+ /*0x30*/ "GlobalToLocal",
+ /*0x31*/ "LocalToGlobal",
+ /*0x32*/ "MapKeyToDir",
+ /*0x33*/ "HaveMouse",
+ /*0x34*/ "SetCursor",
+ /*0x35*/ "VibrateMouse",
+ /*0x36*/ "SaveGame",
+ /*0x37*/ "RestoreGame",
+ /*0x38*/ "RestartGame",
+ /*0x39*/ "GameIsRestarting",
+ /*0x3a*/ "MakeSaveCatName",
+ /*0x3b*/ "MakeSaveFileName",
+ /*0x3c*/ "GetSaveFiles",
+ /*0x3d*/ "GetSaveDir",
+ /*0x3e*/ "CheckSaveGame",
+ /*0x3f*/ "CheckFreeSpace",
+ /*0x40*/ "DoSound",
+ /*0x41*/ "DoAudio",
+ /*0x42*/ "DoSync",
+ /*0x43*/ "NewList",
+ /*0x44*/ "DisposeList",
+ /*0x45*/ "NewNode",
+ /*0x46*/ "FirstNode",
+ /*0x47*/ "LastNode",
+ /*0x48*/ "EmptyList",
+ /*0x49*/ "NextNode",
+ /*0x4a*/ "PrevNode",
+ /*0x4b*/ "NodeValue",
+ /*0x4c*/ "AddAfter",
+ /*0x4d*/ "AddToFront",
+ /*0x4e*/ "AddToEnd",
+ /*0x4f*/ "Dummy",
+ /*0x50*/ "Dummy",
+ /*0x51*/ "FindKey",
+ /*0x52*/ "Dummy",
+ /*0x53*/ "Dummy",
+ /*0x54*/ "Dummy",
+ /*0x55*/ "DeleteKey",
+ /*0x56*/ "Dummy",
+ /*0x57*/ "Dummy",
+ /*0x58*/ "ListAt",
+ /*0x59*/ "ListIndexOf",
+ /*0x5a*/ "ListEachElementDo",
+ /*0x5b*/ "ListFirstTrue",
+ /*0x5c*/ "ListAllTrue",
+ /*0x5d*/ "Random",
+ /*0x5e*/ "Abs",
+ /*0x5f*/ "Sqrt",
+ /*0x60*/ "GetAngle",
+ /*0x61*/ "GetDistance",
+ /*0x62*/ "ATan",
+ /*0x63*/ "SinMult",
+ /*0x64*/ "CosMult",
+ /*0x65*/ "SinDiv",
+ /*0x66*/ "CosDiv",
+ /*0x67*/ "GetTime",
+ /*0x68*/ "Platform",
+ /*0x69*/ "BaseSetter",
+ /*0x6a*/ "DirLoop",
+ /*0x6b*/ "CantBeHere",
+ /*0x6c*/ "InitBresen",
+ /*0x6d*/ "DoBresen",
+ /*0x6e*/ "SetJump",
+ /*0x6f*/ "AvoidPath",
+ /*0x70*/ "InPolygon",
+ /*0x71*/ "MergePoly",
+ /*0x72*/ "SetDebug",
+ /*0x73*/ "InspectObject",
+ /*0x74*/ "MemoryInfo",
+ /*0x75*/ "Profiler",
+ /*0x76*/ "Record",
+ /*0x77*/ "PlayBack",
+ /*0x78*/ "MonoOut",
+ /*0x79*/ "SetFatalStr",
+ /*0x7a*/ "GetCWD",
+ /*0x7b*/ "ValidPath",
+ /*0x7c*/ "FileIO",
+ /*0x7d*/ "Dummy",
+ /*0x7e*/ "DeviceInfo",
+ /*0x7f*/ "Palette",
+ /*0x80*/ "PalVary",
+ /*0x81*/ "PalCycle",
+ /*0x82*/ "Array",
+ /*0x83*/ "String",
+ /*0x84*/ "RemapColors",
+ /*0x85*/ "IntegrityChecking",
+ /*0x86*/ "CheckIntegrity",
+ /*0x87*/ "ObjectIntersect",
+ /*0x88*/ "MarkMemory",
+ /*0x89*/ "TextWidth",
+ /*0x8a*/ "PointSize",
+
+ // GK2 Demo (and similar) only kernel functions
+ /*0x8b*/ "AddLine",
+ /*0x8c*/ "DeleteLine",
+ /*0x8d*/ "UpdateLine",
+ /*0x8e*/ "AddPolygon",
+ /*0x8f*/ "DeletePolygon",
+ /*0x90*/ "UpdatePolygon",
+ /*0x91*/ "Bitmap",
+ /*0x92*/ "ScrollWindow",
+ /*0x93*/ "SetFontRes",
+ /*0x94*/ "MovePlaneItems",
+ /*0x95*/ "PreloadResource",
+ /*0x96*/ "Dummy",
+ /*0x97*/ "ResourceTrack",
+ /*0x98*/ "CheckCDisc",
+ /*0x99*/ "GetSaveCDisc",
+ /*0x9a*/ "TestPoly",
+ /*0x9b*/ "WinHelp",
+ /*0x9c*/ "LoadChunk",
+ /*0x9d*/ "SetPalStyleRange",
+ /*0x9e*/ "AddPicAt",
+ /*0x9f*/ "MessageBox"
+};
+
+static const char *sci21_default_knames[] = {
+ /*0x00*/ "Load",
+ /*0x01*/ "UnLoad",
+ /*0x02*/ "ScriptID",
+ /*0x03*/ "DisposeScript",
+ /*0x04*/ "Lock",
+ /*0x05*/ "ResCheck",
+ /*0x06*/ "Purge",
+ /*0x07*/ "SetLanguage",
+ /*0x08*/ "Dummy",
+ /*0x09*/ "Dummy",
+ /*0x0a*/ "Clone",
+ /*0x0b*/ "DisposeClone",
+ /*0x0c*/ "RespondsTo",
+ /*0x0d*/ "FindSelector",
+ /*0x0e*/ "FindClass",
+ /*0x0f*/ "Dummy",
+ /*0x10*/ "Dummy",
+ /*0x11*/ "Dummy",
+ /*0x12*/ "Dummy",
+ /*0x13*/ "Dummy",
+ /*0x14*/ "SetNowSeen",
+ /*0x15*/ "NumLoops",
+ /*0x16*/ "NumCels",
+ /*0x17*/ "IsOnMe",
+ /*0x18*/ "AddMagnify",
+ /*0x19*/ "DeleteMagnify",
+ /*0x1a*/ "CelRect",
+ /*0x1b*/ "BaseLineSpan",
+ /*0x1c*/ "CelWide",
+ /*0x1d*/ "CelHigh",
+ /*0x1e*/ "AddScreenItem",
+ /*0x1f*/ "DeleteScreenItem",
+ /*0x20*/ "UpdateScreenItem",
+ /*0x21*/ "FrameOut",
+ /*0x22*/ "CelInfo",
+ /*0x23*/ "Bitmap",
+ /*0x24*/ "CelLink",
+ /*0x25*/ "Dummy",
+ /*0x26*/ "Dummy",
+ /*0x27*/ "Dummy",
+ /*0x28*/ "AddPlane",
+ /*0x29*/ "DeletePlane",
+ /*0x2a*/ "UpdatePlane",
+ /*0x2b*/ "RepaintPlane",
+ /*0x2c*/ "GetHighPlanePri",
+ /*0x2d*/ "GetHighItemPri",
+ /*0x2e*/ "SetShowStyle",
+ /*0x2f*/ "ShowStylePercent",
+ /*0x30*/ "SetScroll",
+ /*0x31*/ "MovePlaneItems",
+ /*0x32*/ "ShakeScreen",
+ /*0x33*/ "Dummy",
+ /*0x34*/ "Dummy",
+ /*0x35*/ "Dummy",
+ /*0x36*/ "Dummy",
+ /*0x37*/ "IsHiRes",
+ /*0x38*/ "SetVideoMode",
+ /*0x39*/ "ShowMovie",
+ /*0x3a*/ "Robot",
+ /*0x3b*/ "CreateTextBitmap",
+ /*0x3c*/ "Random",
+ /*0x3d*/ "Abs",
+ /*0x3e*/ "Sqrt",
+ /*0x3f*/ "GetAngle",
+ /*0x40*/ "GetDistance",
+ /*0x41*/ "ATan",
+ /*0x42*/ "SinMult",
+ /*0x43*/ "CosMult",
+ /*0x44*/ "SinDiv",
+ /*0x45*/ "CosDiv",
+ /*0x46*/ "Text",
+ /*0x47*/ "Dummy",
+ /*0x48*/ "Message",
+ /*0x49*/ "Font",
+ /*0x4a*/ "EditText",
+ /*0x4b*/ "InputText",
+ /*0x4c*/ "ScrollWindow",
+ /*0x4d*/ "Dummy",
+ /*0x4e*/ "Dummy",
+ /*0x4f*/ "Dummy",
+ /*0x50*/ "GetEvent",
+ /*0x51*/ "GlobalToLocal",
+ /*0x52*/ "LocalToGlobal",
+ /*0x53*/ "MapKeyToDir",
+ /*0x54*/ "HaveMouse",
+ /*0x55*/ "SetCursor",
+ /*0x56*/ "VibrateMouse",
+ /*0x57*/ "Dummy",
+ /*0x58*/ "Dummy",
+ /*0x59*/ "Dummy",
+ /*0x5a*/ "List",
+ /*0x5b*/ "Array",
+ /*0x5c*/ "String",
+ /*0x5d*/ "FileIO",
+ /*0x5e*/ "BaseSetter",
+ /*0x5f*/ "DirLoop",
+ /*0x60*/ "CantBeHere",
+ /*0x61*/ "InitBresen",
+ /*0x62*/ "DoBresen",
+ /*0x63*/ "SetJump",
+ /*0x64*/ "AvoidPath",
+ /*0x65*/ "InPolygon",
+ /*0x66*/ "MergePoly",
+ /*0x67*/ "ObjectIntersect",
+ /*0x68*/ "Dummy",
+ /*0x69*/ "MemoryInfo",
+ /*0x6a*/ "DeviceInfo",
+ /*0x6b*/ "Palette",
+ /*0x6c*/ "PalVary",
+ /*0x6d*/ "PalCycle",
+ /*0x6e*/ "RemapColors",
+ /*0x6f*/ "AddLine",
+ /*0x70*/ "DeleteLine",
+ /*0x71*/ "UpdateLine",
+ /*0x72*/ "AddPolygon",
+ /*0x73*/ "DeletePolygon",
+ /*0x74*/ "UpdatePolygon",
+ /*0x75*/ "DoSound",
+ /*0x76*/ "DoAudio",
+ /*0x77*/ "DoSync",
+ /*0x78*/ "Save",
+ /*0x79*/ "GetTime",
+ /*0x7a*/ "Platform",
+ /*0x7b*/ "CD",
+ /*0x7c*/ "SetQuitStr",
+ /*0x7d*/ "GetConfig",
+ /*0x7e*/ "Table",
+ /*0x7f*/ "WinHelp", // Windows only
+ /*0x80*/ "Dummy",
+ /*0x81*/ "Dummy",
+ /*0x82*/ "Dummy",
+ /*0x83*/ "PrintDebug", // used by Shivers 2 (demo and full)
+ /*0x84*/ "Dummy",
+ /*0x85*/ "Dummy",
+ /*0x86*/ "Dummy",
+ /*0x87*/ "Dummy",
+ /*0x88*/ "Dummy",
+ /*0x89*/ "Dummy",
+ /*0x8a*/ "LoadChunk",
+ /*0x8b*/ "SetPalStyleRange",
+ /*0x8c*/ "AddPicAt",
+ /*0x8d*/ "Dummy",
+ /*0x8e*/ "NewRoom",
+ /*0x8f*/ "Dummy",
+ /*0x90*/ "Priority",
+ /*0x91*/ "MorphOn",
+ /*0x92*/ "PlayVMD",
+ /*0x93*/ "SetHotRectangles",
+ /*0x94*/ "MulDiv",
+ /*0x95*/ "GetSierraProfileInt", // Windows only
+ /*0x96*/ "GetSierraProfileString", // Windows only
+ /*0x97*/ "SetWindowsOption", // Windows only
+ /*0x98*/ "GetWindowsOption", // Windows only
+ /*0x99*/ "WinDLL", // Windows only
+ /*0x9a*/ "Dummy",
+ /*0x9b*/ "Dummy",
+ /*0x9c*/ "DeletePic"
+};
+
+#endif
+
+#define END Script_None
+
+opcode_format g_opcode_formats[128][4] = {
+ /*00*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*04*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*08*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*0C*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*10*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*14*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
+ /*18*/
+ {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
+ /*1C*/
+ {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
+ /*20*/
+ {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
+ /*24 (24=ret)*/
+ {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
+ /*28*/
+ {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
+ /*2C*/
+ {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
+ /*30*/
+ {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
+ /*34*/
+ {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
+ /*38*/
+ {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
+ /*3C*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_Word},
+ /*40-4F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*50-5F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*60-6F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*70-7F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
+};
+#undef END
+
+} // End of namespace Sci
+
+#endif // SCI_ENGINE_KERNEL_TABLES_H
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index 156035b30d..3395811700 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -31,8 +31,6 @@
#include "sci/console.h"
#include "sci/debug.h" // for g_debug_simulated_key
#include "sci/event.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/cursor.h"
@@ -43,50 +41,58 @@ namespace Sci {
reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
int mask = argv[0].toUint16();
reg_t obj = argv[1];
- sciEvent curEvent;
+ SciEvent curEvent;
int oldx, oldy;
int modifier_mask = getSciVersion() <= SCI_VERSION_01 ? SCI_KEYMOD_ALL : SCI_KEYMOD_NO_FOOLOCK;
SegManager *segMan = s->_segMan;
Common::Point mousePos;
+ // Limit the mouse cursor position, if necessary
+ g_sci->_gfxCursor->refreshPosition();
mousePos = g_sci->_gfxCursor->getPosition();
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2_1)
+ g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x);
+#endif
// If there's a simkey pending, and the game wants a keyboard event, use the
// simkey instead of a normal event
if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) {
- PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
- PUT_SEL32V(segMan, obj, SELECTOR(message), g_debug_simulated_key);
- PUT_SEL32V(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on
- PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y);
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
+ writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key);
+ writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on
+ writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y);
g_debug_simulated_key = 0;
return make_reg(0, 1);
}
oldx = mousePos.x;
oldy = mousePos.y;
- curEvent = s->_event->get(mask);
+ curEvent = g_sci->getEventManager()->getSciEvent(mask);
- if (s->_voc)
- s->_voc->parser_event = NULL_REG; // Invalidate parser event
+ if (g_sci->getVocabulary())
+ g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event
- PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y);
+ writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y);
//s->_gui->moveCursor(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y);
switch (curEvent.type) {
case SCI_EVENT_QUIT:
- quit_vm();
+ s->abortScriptProcessing = kAbortQuitGame; // Terminate VM
+ g_sci->_debugState.seeking = kDebugSeekNothing;
+ g_sci->_debugState.runningStep = 0;
break;
case SCI_EVENT_KEYBOARD:
- PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
s->r_acc = make_reg(0, 1);
- PUT_SEL32V(segMan, obj, SELECTOR(message), curEvent.character);
+ writeSelectorValue(segMan, obj, SELECTOR(message), curEvent.character);
// We only care about the translated character
- PUT_SEL32V(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask);
+ writeSelectorValue(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask);
break;
case SCI_EVENT_MOUSE_RELEASE:
@@ -111,9 +117,9 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
break;
}
- PUT_SEL32V(segMan, obj, SELECTOR(type), curEvent.type);
- PUT_SEL32V(segMan, obj, SELECTOR(message), 0);
- PUT_SEL32V(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask);
+ writeSelectorValue(segMan, obj, SELECTOR(type), curEvent.type);
+ writeSelectorValue(segMan, obj, SELECTOR(message), 0);
+ writeSelectorValue(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask);
s->r_acc = make_reg(0, 1);
}
break;
@@ -122,12 +128,12 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
s->r_acc = NULL_REG; // Unknown or no event
}
- if ((s->r_acc.offset) && (g_debugState.stopOnEvent)) {
- g_debugState.stopOnEvent = false;
+ if ((s->r_acc.offset) && (g_sci->_debugState.stopOnEvent)) {
+ g_sci->_debugState.stopOnEvent = false;
- // A SCI event occured, and we have been asked to stop, so open the debug console
+ // A SCI event occurred, and we have been asked to stop, so open the debug console
Console *con = g_sci->getSciDebugger();
- con->DebugPrintf("SCI event occured: ");
+ con->DebugPrintf("SCI event occurred: ");
switch (curEvent.type) {
case SCI_EVENT_QUIT:
con->DebugPrintf("quit event\n");
@@ -147,16 +153,14 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
con->onFrame();
}
-#ifndef USE_OLD_MUSIC_FUNCTIONS
if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) {
- // If we're running a SCI0 game, update the sound cues, to compensate
- // for the fact that SCI0 does not poll to update the sound cues itself,
- // like SCI01 and later do with cmdUpdateSoundCues. kGetEvent is called
- // quite often, so emulate the SCI01 behavior of cmdUpdateSoundCues with
- // this call
- s->_soundCmd->updateSci0Cues();
+ // If we're running a sound-SCI0 game, update the sound cues, to
+ // compensate for the fact that sound-SCI0 does not poll to update
+ // the sound cues itself, like sound-SCI1 and later do with
+ // cmdUpdateSoundCues. kGetEvent is called quite often, so emulate
+ // the sound-SCI1 behavior of cmdUpdateSoundCues with this call
+ g_sci->_soundCmd->updateSci0Cues();
}
-#endif
return s->r_acc;
}
@@ -165,9 +169,9 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
SegManager *segMan = s->_segMan;
- if (GET_SEL32V(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard
+ if (readSelectorValue(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard
int mover = -1;
- switch (GET_SEL32V(segMan, obj, SELECTOR(message))) {
+ switch (readSelectorValue(segMan, obj, SELECTOR(message))) {
case SCI_KEY_HOME:
mover = 8;
break;
@@ -201,8 +205,11 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
}
if (mover >= 0) {
- PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK);
- PUT_SEL32V(segMan, obj, SELECTOR(message), mover);
+ if (g_sci->getEventManager()->getUsesNewKeyboardDirectionType())
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD | SCI_EVENT_DIRECTION);
+ else
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_DIRECTION);
+ writeSelectorValue(segMan, obj, SELECTOR(message), mover);
return make_reg(0, 1);
} else
return NULL_REG;
@@ -212,18 +219,18 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
}
reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
- reg_t obj = argc ? argv[0] : NULL_REG; // Can this really happen? Lars
+ reg_t obj = argv[0];
reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
if (obj.segment) {
- int16 x = GET_SEL32V(segMan, obj, SELECTOR(x));
- int16 y = GET_SEL32V(segMan, obj, SELECTOR(y));
+ int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
+ int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject);
- PUT_SEL32V(segMan, obj, SELECTOR(x), x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), y);
+ writeSelectorValue(segMan, obj, SELECTOR(x), x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), y);
}
return s->r_acc;
@@ -231,18 +238,18 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
}
reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) {
- reg_t obj = argc ? argv[0] : NULL_REG; // Can this really happen? Lars
+ reg_t obj = argv[0];
reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
if (obj.segment) {
- int16 x = GET_SEL32V(segMan, obj, SELECTOR(x));
- int16 y = GET_SEL32V(segMan, obj, SELECTOR(y));
+ int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
+ int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject);
- PUT_SEL32V(segMan, obj, SELECTOR(x), x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), y);
+ writeSelectorValue(segMan, obj, SELECTOR(x), x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), y);
}
return s->r_acc;
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index e6b9a5388c..39c32ccc68 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -36,14 +36,13 @@
namespace Sci {
-enum {
- MAX_SAVEGAME_NR = 20 /**< Maximum number of savegames */
-};
-
struct SavegameDesc {
- int id;
+ uint id;
+ int virtualId; // straight numbered, according to id but w/o gaps
int date;
int time;
+ int version;
+ char name[SCI_MAX_SAVENAME_LENGTH];
};
/*
@@ -99,7 +98,7 @@ enum {
-void file_open(EngineState *s, const char *filename, int mode) {
+reg_t file_open(EngineState *s, const char *filename, int mode) {
// QfG3 character import prepends /\ to the filenames.
if (filename[0] == '/' && filename[1] == '\\')
filename += 2;
@@ -113,15 +112,17 @@ void file_open(EngineState *s, const char *filename, int mode) {
if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
// Try to open file, abort if not possible
inFile = saveFileMan->openForLoading(wrappedName);
- // If no matching savestate exists: fall back to reading from a regular file
+ // If no matching savestate exists: fall back to reading from a regular
+ // file
if (!inFile)
inFile = SearchMan.createReadStreamForMember(englishName);
- // Special case for LSL3: It tries to create a new dummy file, LARRY3.DRV
- // Apparently, if the file doesn't exist here, it should be created. The game
- // scripts then go ahead and fill its contents with data. It seems to be a similar
- // case as the dummy MEMORY.DRV file in LSL5, but LSL5 creates the file if it can't
- // find it with a separate call to file_open()
+ // Special case for LSL3: It tries to create a new dummy file,
+ // LARRY3.DRV. Apparently, if the file doesn't exist here, it should be
+ // created. The game scripts then go ahead and fill its contents with
+ // data. It seems to be a similar case as the dummy MEMORY.DRV file in
+ // LSL5, but LSL5 creates the file if it can't find it with a separate
+ // call to file_open().
if (!inFile && englishName == "LARRY3.DRV") {
outFile = saveFileMan->openForSaving(wrappedName);
outFile->finalize();
@@ -131,29 +132,29 @@ void file_open(EngineState *s, const char *filename, int mode) {
}
if (!inFile)
- warning("file_open(_K_FILE_MODE_OPEN_OR_FAIL) failed to open file '%s'", englishName.c_str());
+ debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str());
} else if (mode == _K_FILE_MODE_CREATE) {
// Create the file, destroying any content it might have had
outFile = saveFileMan->openForSaving(wrappedName);
if (!outFile)
- warning("file_open(_K_FILE_MODE_CREATE) failed to create file '%s'", englishName.c_str());
+ debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
} else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) {
// Try to open file, create it if it doesn't exist
outFile = saveFileMan->openForSaving(wrappedName);
if (!outFile)
- warning("file_open(_K_FILE_MODE_CREATE) failed to create file '%s'", englishName.c_str());
- // QfG1 opens the character export file with _K_FILE_MODE_CREATE first, closes it immediately and opens it again
- // with this here
- // Perhaps other games use this for read access as well
- // I guess changing this whole code into using virtual files and writing them after close would be more appropriate
+ debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
+ // QfG1 opens the character export file with _K_FILE_MODE_CREATE first,
+ // closes it immediately and opens it again with this here. Perhaps
+ // other games use this for read access as well. I guess changing this
+ // whole code into using virtual files and writing them after close
+ // would be more appropriate.
} else {
error("file_open: unsupported mode %d (filename '%s')", mode, englishName.c_str());
}
if (!inFile && !outFile) { // Failed
- debug(3, "file_open() failed");
- s->r_acc = SIGNAL_REG;
- return;
+ debugC(2, kDebugLevelFile, " -> file_open() failed");
+ return SIGNAL_REG;
}
// Find a free file handle
@@ -161,7 +162,8 @@ void file_open(EngineState *s, const char *filename, int mode) {
while ((handle < s->_fileHandles.size()) && s->_fileHandles[handle].isOpen())
handle++;
- if (handle == s->_fileHandles.size()) { // Hit size limit => Allocate more space
+ if (handle == s->_fileHandles.size()) {
+ // Hit size limit => Allocate more space
s->_fileHandles.resize(s->_fileHandles.size() + 1);
}
@@ -169,18 +171,16 @@ void file_open(EngineState *s, const char *filename, int mode) {
s->_fileHandles[handle]._out = outFile;
s->_fileHandles[handle]._name = englishName;
- s->r_acc = make_reg(0, handle);
-
- debug(3, " -> opened file '%s' with handle %d", englishName.c_str(), handle);
+ debugC(2, kDebugLevelFile, " -> opened file '%s' with handle %d", englishName.c_str(), handle);
+ return make_reg(0, handle);
}
reg_t kFOpen(EngineState *s, int argc, reg_t *argv) {
Common::String name = s->_segMan->getString(argv[0]);
int mode = argv[1].toUint16();
- debug(3, "kFOpen(%s,0x%x)", name.c_str(), mode);
- file_open(s, name.c_str(), mode);
- return s->r_acc;
+ debugC(2, kDebugLevelFile, "kFOpen(%s,0x%x)", name.c_str(), mode);
+ return file_open(s, name.c_str(), mode);
}
static FileHandle *getFileFromHandle(EngineState *s, uint handle) {
@@ -198,7 +198,7 @@ static FileHandle *getFileFromHandle(EngineState *s, uint handle) {
}
reg_t kFClose(EngineState *s, int argc, reg_t *argv) {
- debug(3, "kFClose(%d)", argv[0].toUint16());
+ debugC(2, kDebugLevelFile, "kFClose(%d)", argv[0].toUint16());
if (argv[0] != SIGNAL_REG) {
FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
if (f)
@@ -219,8 +219,6 @@ reg_t kFPuts(EngineState *s, int argc, reg_t *argv) {
}
static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
- debugC(2, kDebugLevelFile, "FGets'ing %d bytes from handle %d", maxsize, handle);
-
FileHandle *f = getFileFromHandle(s, handle);
if (!f)
return;
@@ -242,79 +240,7 @@ static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
*dest = f->_in->readByte();
}
- debugC(2, kDebugLevelFile, "FGets'ed \"%s\"", dest);
-}
-
-static int _savegame_index_struct_compare(const void *a, const void *b) {
- const SavegameDesc *A = (const SavegameDesc *)a;
- const SavegameDesc *B = (const SavegameDesc *)b;
-
- if (B->date != A->date)
- return B->date - A->date;
- return B->time - A->time;
-}
-
-void listSavegames(Common::Array<SavegameDesc> &saves) {
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
-
- // Load all saves
- Common::StringArray saveNames = saveFileMan->listSavefiles(g_sci->getSavegamePattern());
-
- for (Common::StringArray::const_iterator iter = saveNames.begin(); iter != saveNames.end(); ++iter) {
- Common::String filename = *iter;
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
- // invalid
- delete in;
- continue;
- }
- delete in;
-
- SavegameDesc desc;
- desc.id = strtol(filename.end() - 3, NULL, 10);
- desc.date = meta.savegame_date;
- // We need to fix date in here, because we save DDMMYYYY instead of YYYYMMDD, so sorting wouldnt work
- desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24);
- desc.time = meta.savegame_time;
- debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id);
-
- saves.push_back(desc);
- }
- }
-
- // Sort the list by creation date of the saves
- qsort(saves.begin(), saves.size(), sizeof(SavegameDesc), _savegame_index_struct_compare);
-}
-
-bool Console::cmdListSaves(int argc, const char **argv) {
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
-
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
-
- for (uint i = 0; i < saves.size(); i++) {
- Common::String filename = g_sci->getSavegameName(saves[i].id);
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
- // invalid
- delete in;
- continue;
- }
-
- if (!meta.savegame_name.empty()) {
- if (meta.savegame_name.lastChar() == '\n')
- meta.savegame_name.deleteLastChar();
-
- DebugPrintf("%s: '%s'\n", filename.c_str(), meta.savegame_name.c_str());
- }
- delete in;
- }
- }
- return true;
+ debugC(2, kDebugLevelFile, " -> FGets'ed \"%s\"", dest);
}
reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
@@ -322,7 +248,7 @@ reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
char *buf = new char[maxsize];
int handle = argv[2].toUint16();
- debug(3, "kFGets(%d,%d)", handle, maxsize);
+ debugC(2, kDebugLevelFile, "kFGets(%d, %d)", handle, maxsize);
fgets_wrapper(s, buf, maxsize, handle);
s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
return argv[0];
@@ -337,11 +263,14 @@ reg_t kGetCWD(EngineState *s, int argc, reg_t *argv) {
// TODO/FIXME: Is "/" a good value? Maybe "" or "." or "C:\" are better?
s->_segMan->strcpy(argv[0], "/");
- debug(3, "kGetCWD() -> %s", "/");
+ debugC(2, kDebugLevelFile, "kGetCWD() -> %s", "/");
return argv[0];
}
+static void listSavegames(Common::Array<SavegameDesc> &saves);
+static int findSavegame(Common::Array<SavegameDesc> &saves, uint saveId);
+
enum {
K_DEVICE_INFO_GET_DEVICE = 0,
K_DEVICE_INFO_GET_CURRENT_DEVICE = 1,
@@ -353,6 +282,14 @@ enum {
};
reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getGameId() == GID_FANMADE && argc == 1) {
+ // WORKAROUND: The fan game script library calls kDeviceInfo with one parameter.
+ // According to the scripts, it wants to call CurDevice. However, it fails to
+ // provide the subop to the function.
+ s->_segMan->strcpy(argv[0], "/");
+ return s->r_acc;
+ }
+
int mode = argv[0].toUint16();
switch (mode) {
@@ -400,21 +337,25 @@ reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
break;
case K_DEVICE_INFO_GET_SAVEFILE_NAME: {
Common::String game_prefix = s->_segMan->getString(argv[2]);
- int savegame_id = argv[3].toUint16();
+ uint virtualId = argv[3].toUint16();
s->_segMan->strcpy(argv[1], "__throwaway");
- debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix.c_str(), savegame_id, "__throwaway");
+ debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix.c_str(), virtualId, "__throwaway");
+ if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
+ error("kDeviceInfo(deleteSave): invalid savegame-id specified");
+ uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- int savedir_nr = saves[savegame_id].id;
- Common::String filename = g_sci->getSavegameName(savedir_nr);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- saveFileMan->removeSavefile(filename);
+ if (findSavegame(saves, savegameId) != -1) {
+ // Confirmed that this id still lives...
+ Common::String filename = g_sci->getSavegameName(savegameId);
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ saveFileMan->removeSavefile(filename);
}
break;
+ }
default:
- // TODO: Not all sub-commands are handled. E.g. KQ5CD calls sub-command 5
- warning("Unknown DeviceInfo() sub-command: %d", mode);
+ error("Unknown DeviceInfo() sub-command: %d", mode);
break;
}
@@ -428,155 +369,237 @@ reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0]));
#endif
- return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR);
+ return make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_SAVEDIR);
}
reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
-#ifdef ENABLE_SCI32
- // TODO: SCI32 uses a parameter here.
- if (argc > 1)
- warning("kCheckFreeSpace called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[1]));
-#endif
+ if (argc > 1) {
+ // SCI1.1/SCI32
+ // TODO: don't know if those are right for SCI32 as well
+ // Please note that sierra sci supported both calls either w/ or w/o opcode in SCI1.1
+ switch (argv[1].toUint16()) {
+ case 0: // return saved game size
+ return make_reg(0, 0); // we return 0
+
+ case 1: // return free harddisc space (shifted right somehow)
+ return make_reg(0, 0x7fff); // we return maximum
+
+ case 2: // same as call w/o opcode
+ break;
+ return make_reg(0, 1);
+
+ default:
+ error("kCheckFreeSpace: called with unknown sub-op %d", argv[1].toUint16());
+ }
+ }
Common::String path = s->_segMan->getString(argv[0]);
debug(3, "kCheckFreeSpace(%s)", path.c_str());
- // We simply always pretend that there is enough space.
- // The alternative would be to write a big test file, which is not nice
- // on systems where doing so is very slow.
+ // We simply always pretend that there is enough space. The alternative
+ // would be to write a big test file, which is not nice on systems where
+ // doing so is very slow.
return make_reg(0, 1);
}
+static bool _savegame_sort_byDate(const SavegameDesc &l, const SavegameDesc &r) {
+ if (l.date != r.date)
+ return (l.date > r.date);
+ return (l.time > r.time);
+}
+
+// Create a sorted array containing all found savedgames
+static void listSavegames(Common::Array<SavegameDesc> &saves) {
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+
+ // Load all saves
+ Common::StringArray saveNames = saveFileMan->listSavefiles(g_sci->getSavegamePattern());
+
+ for (Common::StringArray::const_iterator iter = saveNames.begin(); iter != saveNames.end(); ++iter) {
+ Common::String filename = *iter;
+ Common::SeekableReadStream *in;
+ if ((in = saveFileMan->openForLoading(filename))) {
+ SavegameMetadata meta;
+ if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) {
+ // invalid
+ delete in;
+ continue;
+ }
+ delete in;
+
+ SavegameDesc desc;
+ desc.id = strtol(filename.end() - 3, NULL, 10);
+ desc.date = meta.savegame_date;
+ // We need to fix date in here, because we save DDMMYYYY instead of
+ // YYYYMMDD, so sorting wouldn't work
+ desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24);
+ desc.time = meta.savegame_time;
+ desc.version = meta.savegame_version;
+
+ if (meta.savegame_name.lastChar() == '\n')
+ meta.savegame_name.deleteLastChar();
+
+ Common::strlcpy(desc.name, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH);
+
+ debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id);
+
+ saves.push_back(desc);
+ }
+ }
+
+ // Sort the list by creation date of the saves
+ Common::sort(saves.begin(), saves.end(), _savegame_sort_byDate);
+}
+
+// Find a savedgame according to virtualId and return the position within our array
+static int findSavegame(Common::Array<SavegameDesc> &saves, uint savegameId) {
+ for (uint saveNr = 0; saveNr < saves.size(); saveNr++) {
+ if (saves[saveNr].id == savegameId)
+ return saveNr;
+ }
+ return -1;
+}
+
+// The scripts get IDs ranging from 1000->1999, because the scripts require us to assign unique ids THAT EVEN STAY BETWEEN
+// SAVES and the scripts also use "saves-count + 1" to create a new savedgame slot.
+// SCI1.1 actually recycles ids, in that case we will currently get "0".
+// This behaviour is required especially for LSL6. In this game, it's possible to quick save. The scripts will use
+// the last-used id for that feature. If we don't assign sticky ids, the feature will overwrite different saves all the
+// time. And sadly we can't just use the actual filename ids directly, because of the creation method for new slots.
+
+bool Console::cmdListSaves(int argc, const char **argv) {
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+
+ for (uint i = 0; i < saves.size(); i++) {
+ Common::String filename = g_sci->getSavegameName(saves[i].id);
+ DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name);
+ }
+
+ return true;
+}
+
reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = s->_segMan->getString(argv[0]);
- int savedir_nr = argv[1].toUint16();
+ uint16 virtualId = argv[1].toUint16();
- debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), savedir_nr);
+ debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), virtualId);
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- savedir_nr = saves[savedir_nr].id;
-
- if (savedir_nr > MAX_SAVEGAME_NR - 1) {
+ // we allow 0 (happens in QfG2 when trying to restore from an empty saved game list) and return false in that case
+ if (virtualId == 0)
return NULL_REG;
- }
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- Common::String filename = g_sci->getSavegameName(savedir_nr);
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- // found a savegame file
+ // Find saved-game
+ if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
+ error("kCheckSaveGame: called with invalid savegameId!");
+ uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+ int savegameNr = findSavegame(saves, savegameId);
+ if (savegameNr == -1)
+ return NULL_REG;
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
- // invalid
- s->r_acc = make_reg(0, 0);
- } else {
- s->r_acc = make_reg(0, 1);
- }
- delete in;
- } else {
- s->r_acc = make_reg(0, 1);
- }
+ // Check for compatible savegame version
+ int ver = saves[savegameNr].version;
+ if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
+ return NULL_REG;
- return s->r_acc;
+ // Otherwise we assume the savegame is OK
+ return TRUE_REG;
}
reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = s->_segMan->getString(argv[0]);
- reg_t nametarget = argv[1];
- reg_t *nameoffsets = s->_segMan->derefRegPtr(argv[2], 0);
debug(3, "kGetSaveFiles(%s)", game_id.c_str());
+ // Scripts ask for current save files, we can assume that if afterwards they ask us to create a new slot they really
+ // mean new slot instead of overwriting the old one
+ s->_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
+
Common::Array<SavegameDesc> saves;
listSavegames(saves);
+ uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
- s->r_acc = NULL_REG;
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
- for (uint i = 0; i < saves.size(); i++) {
- Common::String filename = g_sci->getSavegameName(saves[i].id);
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- // found a savegame file
-
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
- // invalid
- delete in;
- continue;
- }
-
- if (!meta.savegame_name.empty()) {
- if (meta.savegame_name.lastChar() == '\n')
- meta.savegame_name.deleteLastChar();
-
- *nameoffsets = s->r_acc; // Store savegame ID
- ++s->r_acc.offset; // Increase number of files found
+ if (!slot) {
+ warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2]));
+ totalSaves = 0;
+ }
- nameoffsets++; // Make sure the next ID string address is written to the next pointer
- Common::String name = meta.savegame_name;
- if (name.size() > SCI_MAX_SAVENAME_LENGTH-1)
- name = Common::String(meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH-1);
- s->_segMan->strcpy(nametarget, name.c_str());
+ const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
+ char *saveNames = new char[bufSize];
+ char *saveNamePtr = saveNames;
- // Increase name offset pointer accordingly
- nametarget.offset += SCI_MAX_SAVENAME_LENGTH;
- }
- delete in;
- }
+ for (uint i = 0; i < totalSaves; i++) {
+ *slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame-id ffs. see above
+ strcpy(saveNamePtr, saves[i].name);
+ saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
}
- //free(gfname);
- s->_segMan->strcpy(nametarget, ""); // Terminate list
+ *saveNamePtr = 0; // Terminate list
- return s->r_acc;
+ s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize);
+ delete[] saveNames;
+
+ return make_reg(0, totalSaves);
}
reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = s->_segMan->getString(argv[0]);
- int savedir_nr = argv[1].toUint16();
- int savedir_id; // Savegame ID, derived from savedir_nr and the savegame ID list
+ uint virtualId = argv[1].toUint16();
Common::String game_description = s->_segMan->getString(argv[2]);
Common::String version;
if (argc > 3)
version = s->_segMan->getString(argv[3]);
- debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), savedir_nr, game_description.c_str(), version.c_str());
+ debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
+
+ // We check here, we don't want to delete a users save in case we are within a kernel function
+ if (s->executionStackBase) {
+ warning("kSaveGame - won't save from within kernel function");
+ return NULL_REG;
+ }
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- if (savedir_nr >= 0 && (uint)savedir_nr < saves.size()) {
- // Overwrite
- savedir_id = saves[savedir_nr].id;
- } else if (savedir_nr >= 0 && savedir_nr < MAX_SAVEGAME_NR) {
- uint i = 0;
-
- savedir_id = 0;
-
- // First, look for holes
- while (i < saves.size()) {
- if (saves[i].id == savedir_id) {
- ++savedir_id;
- i = 0;
- } else
- ++i;
- }
- if (savedir_id >= MAX_SAVEGAME_NR) {
- warning("Internal error: Free savegame ID is %d, shouldn't happen", savedir_id);
+ uint savegameId;
+ if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
+ // savegameId is an actual Id, so search for it just to make sure
+ savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+ if (findSavegame(saves, savegameId) == -1)
return NULL_REG;
+ } else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
+ // virtualId is low, we assume that scripts expect us to create new slot
+ if (virtualId == s->_lastSaveVirtualId) {
+ // if last virtual id is the same as this one, we assume that caller wants to overwrite last save
+ savegameId = s->_lastSaveNewId;
+ } else {
+ uint savegameNr;
+ // savegameId is in lower range, scripts expect us to create a new slot
+ for (savegameId = 0; savegameId < SAVEGAMEID_OFFICIALRANGE_START; savegameId++) {
+ for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) {
+ if (savegameId == saves[savegameNr].id)
+ break;
+ }
+ if (savegameNr == saves.size())
+ break;
+ }
+ if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
+ error("kSavegame: no more savegame slots available");
}
-
- // This loop terminates when savedir_id is not in [x | ex. n. saves [n].id = x]
} else {
- warning("Savegame ID %d is not allowed", savedir_nr);
- return NULL_REG;
+ error("kSaveGame: invalid savegameId used");
}
- Common::String filename = g_sci->getSavegameName(savedir_id);
+ // Save in case caller wants to overwrite last newly created save
+ s->_lastSaveVirtualId = virtualId;
+ s->_lastSaveNewId = savegameId;
+
+ Common::String filename = g_sci->getSavegameName(savegameId);
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
Common::OutSaveFile *out;
if (!(out = saveFileMan->openForSaving(filename))) {
@@ -585,7 +608,7 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
- if (gamestate_save(s, out, game_description.c_str(), version.c_str())) {
+ if (!gamestate_save(s, out, game_description.c_str(), version.c_str())) {
warning("Saving the game failed.");
s->r_acc = NULL_REG;
} else {
@@ -605,35 +628,41 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
- int savedir_nr = argv[1].toUint16();
+ uint savegameId = argv[1].toUint16();
- debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savedir_nr);
+ debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
- if (!argv[0].isNull()) {
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
-
- savedir_nr = saves[savedir_nr].id;
+ if (argv[0].isNull()) {
+ // Loading from the launcher, don't adjust the ID of the saved game
} else {
- // Loading from launcher, no change necessary
+ if ((savegameId < 1000) || (savegameId > 1999)) {
+ warning("Savegame ID %d is not allowed", savegameId);
+ return TRUE_REG;
+ }
+ savegameId -= 1000;
}
- if (savedir_nr > -1) {
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- Common::String filename = g_sci->getSavegameName(savedir_nr);
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- // found a savegame file
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ if (findSavegame(saves, savegameId) == -1) {
+ warning("Savegame ID %d not found", savegameId);
+ return TRUE_REG;
+ }
- gamestate_restore(s, in);
- delete in;
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ Common::String filename = g_sci->getSavegameName(savegameId);
+ Common::SeekableReadStream *in;
+ if ((in = saveFileMan->openForLoading(filename))) {
+ // found a savegame file
- return s->r_acc;
- }
+ gamestate_restore(s, in);
+ delete in;
+
+ return s->r_acc;
}
- s->r_acc = make_reg(0, 1);
- warning("Savegame #%d not found", savedir_nr);
+ s->r_acc = TRUE_REG;
+ warning("Savegame #%d not found", savegameId);
return s->r_acc;
}
@@ -647,28 +676,6 @@ reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, 1);
}
-enum {
- K_FILEIO_OPEN = 0,
- K_FILEIO_CLOSE = 1,
- K_FILEIO_READ_RAW = 2,
- K_FILEIO_WRITE_RAW = 3,
- K_FILEIO_UNLINK = 4,
- K_FILEIO_READ_STRING = 5,
- K_FILEIO_WRITE_STRING = 6,
- K_FILEIO_SEEK = 7,
- K_FILEIO_FIND_FIRST = 8,
- K_FILEIO_FIND_NEXT = 9,
- K_FILEIO_FILE_EXISTS = 10,
- // SCI1.1
- K_FILEIO_RENAME = 11,
- // SCI32
- // 12?
- K_FILEIO_READ_BYTE = 13,
- K_FILEIO_WRITE_BYTE = 14,
- K_FILEIO_READ_WORD = 15,
- K_FILEIO_WRITE_WORD = 16
-};
-
reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan) {
// Verify that we are given a valid buffer
if (!buffer.segment) {
@@ -684,7 +691,8 @@ reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
_savefiles = saveFileMan->listSavefiles(wrappedMask);
- // Reset the list iterator and write the first match to the output buffer, if any.
+ // Reset the list iterator and write the first match to the output buffer,
+ // if any.
_iter = _savefiles.begin();
return nextFile(segMan);
}
@@ -708,258 +716,298 @@ reg_t DirSeeker::nextFile(SegManager *segMan) {
}
reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
- int func_nr = argv[0].toUint16();
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- switch (func_nr) {
- case K_FILEIO_OPEN : {
- Common::String name = s->_segMan->getString(argv[1]);
+reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
+ Common::String name = s->_segMan->getString(argv[0]);
- // SCI32 can call K_FILEIO_OPEN with only two arguments. It seems to just be checking if it exists.
- int mode = (argc < 3) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[2].toUint16();
+ // SCI32 can call K_FILEIO_OPEN with only one argument. It seems to
+ // just be checking if it exists.
+ int mode = (argc < 2) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[1].toUint16();
- // SQ4 floppy prepends /\ to the filenames
- if (name.hasPrefix("/\\")) {
- name.deleteChar(0);
- name.deleteChar(0);
- }
+ // SQ4 floppy prepends /\ to the filenames
+ if (name.hasPrefix("/\\")) {
+ name.deleteChar(0);
+ name.deleteChar(0);
+ }
- // SQ4 floppy attempts to update the savegame index file sq4sg.dir
- // when deleting saved games. We don't use an index file for saving
- // or loading, so just stop the game from modifying the file here
- // in order to avoid having it saved in the ScummVM save directory
- if (name == "sq4sg.dir") {
- debugC(2, kDebugLevelFile, "Not opening unused file sq4sg.dir");
- return SIGNAL_REG;
- }
+ // SQ4 floppy attempts to update the savegame index file sq4sg.dir when
+ // deleting saved games. We don't use an index file for saving or
+ // loading, so just stop the game from modifying the file here in order
+ // to avoid having it saved in the ScummVM save directory.
+ if (name == "sq4sg.dir") {
+ debugC(2, kDebugLevelFile, "Not opening unused file sq4sg.dir");
+ return SIGNAL_REG;
+ }
- if (name.empty()) {
- warning("Attempted to open a file with an empty filename");
- return SIGNAL_REG;
- }
- file_open(s, name.c_str(), mode);
- debug(3, "K_FILEIO_OPEN(%s,0x%x)", name.c_str(), mode);
- break;
+ if (name.empty()) {
+ warning("Attempted to open a file with an empty filename");
+ return SIGNAL_REG;
}
- case K_FILEIO_CLOSE : {
- debug(3, "K_FILEIO_CLOSE(%d)", argv[1].toUint16());
+ debugC(2, kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
+ return file_open(s, name.c_str(), mode);
+}
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (f)
- f->close();
- break;
+reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
+ debugC(2, kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16());
+
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (f) {
+ f->close();
+ return SIGNAL_REG;
}
- case K_FILEIO_READ_RAW : {
- int handle = argv[1].toUint16();
- int size = argv[3].toUint16();
- char *buf = new char[size];
- debug(3, "K_FILEIO_READ_RAW(%d,%d)", handle, size);
+ return NULL_REG;
+}
+reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ int size = argv[2].toUint16();
+ int bytesRead = 0;
+ char *buf = new char[size];
+ debugC(2, kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f) {
- s->r_acc = make_reg(0, f->_in->read(buf, size));
- s->_segMan->memcpy(argv[2], (const byte*)buf, size);
- }
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f) {
+ bytesRead = f->_in->read(buf, size);
+ s->_segMan->memcpy(argv[1], (const byte*)buf, size);
+ }
- delete[] buf;
- break;
+ delete[] buf;
+ return make_reg(0, bytesRead);
+}
+
+reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ int size = argv[2].toUint16();
+ char *buf = new char[size];
+ bool success = false;
+ s->_segMan->memcpy((byte*)buf, argv[1], size);
+ debugC(2, kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
+
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f) {
+ f->_out->write(buf, size);
+ success = true;
}
- case K_FILEIO_WRITE_RAW : {
- int handle = argv[1].toUint16();
- int size = argv[3].toUint16();
- char *buf = new char[size];
- s->_segMan->memcpy((byte*)buf, argv[2], size);
- debug(3, "K_FILEIO_WRITE_RAW(%d,%d)", handle, size);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- f->_out->write(buf, size);
+ delete[] buf;
+ if (success)
+ return NULL_REG;
+ return make_reg(0, 6); // DOS - invalid handle
+}
- delete[] buf;
- break;
+reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
+ Common::String name = s->_segMan->getString(argv[0]);
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ bool result;
+
+ // SQ4 floppy prepends /\ to the filenames
+ if (name.hasPrefix("/\\")) {
+ name.deleteChar(0);
+ name.deleteChar(0);
+ }
+
+ // Special case for SQ4 floppy: This game has hardcoded names for all of
+ // its savegames, and they are all named "sq4sg.xxx", where xxx is the
+ // slot. We just take the slot number here, and delete the appropriate
+ // save game.
+ if (name.hasPrefix("sq4sg.")) {
+ // Special handling for SQ4... get the slot number and construct the
+ // save game name.
+ int slotNum = atoi(name.c_str() + name.size() - 3);
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ int savedir_nr = saves[slotNum].id;
+ name = g_sci->getSavegameName(savedir_nr);
+ result = saveFileMan->removeSavefile(name);
+ } else {
+ const Common::String wrappedName = g_sci->wrapFilename(name);
+ result = saveFileMan->removeSavefile(wrappedName);
}
- case K_FILEIO_UNLINK : {
- Common::String name = s->_segMan->getString(argv[1]);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- // SQ4 floppy prepends /\ to the filenames
- if (name.hasPrefix("/\\")) {
- name.deleteChar(0);
- name.deleteChar(0);
- }
- // Special case for SQ4 floppy: This game has hardcoded names for all of its
- // savegames, and they are all named "sq4sg.xxx", where xxx is the slot. We just
- // take the slot number here, and delete the appropriate save game
- if (name.hasPrefix("sq4sg.")) {
- // Special handling for SQ4... get the slot number and construct the save game name
- int slotNum = atoi(name.c_str() + name.size() - 3);
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
- int savedir_nr = saves[slotNum].id;
- name = g_sci->getSavegameName(savedir_nr);
- saveFileMan->removeSavefile(name);
- } else {
- const Common::String wrappedName = g_sci->wrapFilename(name);
- saveFileMan->removeSavefile(wrappedName);
- }
+ debugC(2, kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
+ if (result)
+ return NULL_REG;
+ return make_reg(0, 2); // DOS - file not found error code
+}
- debug(3, "K_FILEIO_UNLINK(%s)", name.c_str());
+reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
+ int size = argv[1].toUint16();
+ char *buf = new char[size];
+ int handle = argv[2].toUint16();
+ debugC(2, kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size);
- // TODO/FIXME: Should we return something (like, a bool indicating
- // whether deleting the save succeeded or failed)?
- break;
- }
- case K_FILEIO_READ_STRING : {
- int size = argv[2].toUint16();
- char *buf = new char[size];
- int handle = argv[3].toUint16();
- debug(3, "K_FILEIO_READ_STRING(%d,%d)", handle, size);
+ fgets_wrapper(s, buf, size, handle);
+ s->_segMan->memcpy(argv[0], (const byte*)buf, size);
+ delete[] buf;
+ return argv[0];
+}
- fgets_wrapper(s, buf, size, handle);
- s->_segMan->memcpy(argv[1], (const byte*)buf, size);
- delete[] buf;
- return argv[1];
- }
- case K_FILEIO_WRITE_STRING : {
- int handle = argv[1].toUint16();
- int size = argv[3].toUint16();
- Common::String str = s->_segMan->getString(argv[2]);
- debug(3, "K_FILEIO_WRITE_STRING(%d,%d)", handle, size);
-
- // CHECKME: Is the size parameter used at all?
- // In the LSL5 password protection it is zero, and we should
- // then write a full string. (Not sure if it should write the
- // terminating zero.)
-
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- f->_out->write(str.c_str(), str.size());
- break;
- }
- case K_FILEIO_SEEK : {
- int handle = argv[1].toUint16();
- int offset = argv[2].toUint16();
- int whence = argv[3].toUint16();
- debug(3, "K_FILEIO_SEEK(%d,%d,%d)", handle, offset, whence);
+reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ Common::String str = s->_segMan->getString(argv[1]);
+ debugC(2, kDebugLevelFile, "kFileIO(writeString): %d", handle);
+
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f)
+ f->_out->write(str.c_str(), str.size());
+ return NULL_REG;
+ return make_reg(0, 6); // DOS - invalid handle
+}
+
+reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ int offset = argv[1].toUint16();
+ int whence = argv[2].toUint16();
+ debugC(2, kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- s->r_acc = make_reg(0, f->_in->seek(offset, whence));
- break;
- }
- case K_FILEIO_FIND_FIRST : {
- Common::String mask = s->_segMan->getString(argv[1]);
- reg_t buf = argv[2];
- int attr = argv[3].toUint16(); // We won't use this, Win32 might, though...
- debug(3, "K_FILEIO_FIND_FIRST(%s,0x%x)", mask.c_str(), attr);
-
- // We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
- if (mask == "*.*")
- mask = "*";
-
- // QfG3 uses this mask for the character import
- if (mask == "/\\*.*")
- mask = "*";
-//#ifndef WIN32
-// if (mask == "*.*")
-// mask = "*"; // For UNIX
-//#endif
- s->r_acc = s->_dirseeker.firstFile(mask, buf, s->_segMan);
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f)
+ s->r_acc = make_reg(0, f->_in->seek(offset, whence));
+ return SIGNAL_REG;
+}
- break;
- }
- case K_FILEIO_FIND_NEXT : {
- debug(3, "K_FILEIO_FIND_NEXT()");
- s->r_acc = s->_dirseeker.nextFile(s->_segMan);
- break;
+reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv) {
+ Common::String mask = s->_segMan->getString(argv[0]);
+ reg_t buf = argv[1];
+ int attr = argv[2].toUint16(); // We won't use this, Win32 might, though...
+ debugC(2, kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr);
+
+ // QfG3 uses "/\*.*" for the character import, QfG4 uses "/\*"
+ if (mask.hasPrefix("/\\")) {
+ mask.deleteChar(0);
+ mask.deleteChar(0);
}
- case K_FILEIO_FILE_EXISTS : {
- Common::String name = s->_segMan->getString(argv[1]);
- // Check for regular file
- bool exists = Common::File::exists(name);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- const Common::String wrappedName = g_sci->wrapFilename(name);
+ // We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
+ if (mask == "*.*")
+ mask = "*";
+ return s->_dirseeker.firstFile(mask, buf, s->_segMan);
+}
- if (!exists)
- exists = !saveFileMan->listSavefiles(name).empty();
+reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
+ debugC(2, kDebugLevelFile, "kFileIO(findNext)");
+ return s->_dirseeker.nextFile(s->_segMan);
+}
- if (!exists) {
- // Try searching for the file prepending target-
- Common::SeekableReadStream *inFile = saveFileMan->openForLoading(wrappedName);
- exists = (inFile != 0);
- delete inFile;
- }
+reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
+ Common::String name = s->_segMan->getString(argv[0]);
- // Special case for non-English versions of LSL5: The English version of LSL5 calls
- // kFileIO(), case K_FILEIO_OPEN for reading to check if memory.drv exists (which is
- // where the game's password is stored). If it's not found, it calls kFileIO() again,
- // case K_FILEIO_OPEN for writing and creates a new file. Non-English versions call
- // kFileIO(), case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be found.
- // We create a default memory.drv file with no password, so that the game can continue
- if (!exists && name == "memory.drv") {
- // Create a new file, and write the bytes for the empty password string inside
- byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
- Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
- for (int i = 0; i < 10; i++)
- outFile->writeByte(defaultContent[i]);
- outFile->finalize();
- delete outFile;
- exists = true;
- }
+ // Check for regular file
+ bool exists = Common::File::exists(name);
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ const Common::String wrappedName = g_sci->wrapFilename(name);
+
+ if (!exists)
+ exists = !saveFileMan->listSavefiles(name).empty();
+
+ if (!exists) {
+ // Try searching for the file prepending target-
+ Common::SeekableReadStream *inFile = saveFileMan->openForLoading(wrappedName);
+ exists = (inFile != 0);
+ delete inFile;
+ }
+
+ // Special case for non-English versions of LSL5: The English version of
+ // LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
+ // memory.drv exists (which is where the game's password is stored). If
+ // it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
+ // writing and creates a new file. Non-English versions call kFileIO(),
+ // case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
+ // found. We create a default memory.drv file with no password, so that
+ // the game can continue.
+ if (!exists && name == "memory.drv") {
+ // Create a new file, and write the bytes for the empty password
+ // string inside
+ byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
+ Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
+ for (int i = 0; i < 10; i++)
+ outFile->writeByte(defaultContent[i]);
+ outFile->finalize();
+ delete outFile;
+ exists = true;
+ }
+
+ debugC(2, kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
+ return make_reg(0, exists);
+}
- debug(3, "K_FILEIO_FILE_EXISTS(%s) -> %d", name.c_str(), exists);
- return make_reg(0, exists);
- }
- case K_FILEIO_RENAME: {
- Common::String oldName = s->_segMan->getString(argv[1]);
- Common::String newName = s->_segMan->getString(argv[2]);
+reg_t kFileIORename(EngineState *s, int argc, reg_t *argv) {
+ Common::String oldName = s->_segMan->getString(argv[0]);
+ Common::String newName = s->_segMan->getString(argv[1]);
+
+ // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32
+ // returns -1 on fail. We just return -1 for all versions.
+ if (g_engine->getSaveFileManager()->renameSavefile(oldName, newName))
+ return NULL_REG;
+ else
+ return SIGNAL_REG;
+}
- // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32 returns -1 on fail.
- // We just return -1 for all versions.
- if (g_engine->getSaveFileManager()->renameSavefile(oldName, newName))
- return NULL_REG;
- else
- return SIGNAL_REG;
- }
#ifdef ENABLE_SCI32
- case K_FILEIO_READ_BYTE: {
- // Read the byte into the low byte of the accumulator
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (!f)
- return NULL_REG;
-
- return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte());
- }
- case K_FILEIO_WRITE_BYTE: {
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (f)
- f->_out->writeByte(argv[2].toUint16() & 0xff);
- break;
- }
- case K_FILEIO_READ_WORD: {
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (!f)
- return NULL_REG;
-
- return make_reg(0, f->_in->readUint16LE());
- }
- case K_FILEIO_WRITE_WORD: {
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (f)
- f->_out->writeUint16LE(argv[2].toUint16());
- break;
+reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv) {
+ // Read the byte into the low byte of the accumulator
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (!f)
+ return NULL_REG;
+ return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte());
+}
+
+reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv) {
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (f)
+ f->_out->writeByte(argv[1].toUint16() & 0xff);
+ return s->r_acc; // FIXME: does this really doesn't return anything?
+}
+
+reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) {
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (!f)
+ return NULL_REG;
+ return make_reg(0, f->_in->readUint16LE());
+}
+
+reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) {
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (f)
+ f->_out->writeUint16LE(argv[1].toUint16());
+ return s->r_acc; // FIXME: does this really doesn't return anything?
+}
+
+reg_t kCD(EngineState *s, int argc, reg_t *argv) {
+ // TODO: Stub
+ switch (argv[0].toUint16()) {
+ case 0:
+ // Return whether the contents of disc argv[1] is available.
+ return TRUE_REG;
+ default:
+ warning("CD(%d)", argv[0].toUint16());
}
- case 19:
- // TODO: Torin's Passage uses this early on in the Sierra logo
- warning("kFileIO(19)");
- break;
-#endif
+
+ return NULL_REG;
+}
+
+reg_t kSave(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0: // Called by kq7 when starting chapters
+ return SIGNAL_REG;
+ case 2: // GetSaveDir
+ // Yay! Reusing the old kernel function!
+ return kGetSaveDir(s, argc - 1, argv + 1);
+ case 8:
+ // TODO
+ // This function has to return something other than 0 to proceed
+ return s->r_acc;
default:
- error("Unknown FileIO() sub-command: %d", func_nr);
+ warning("Unknown/unhandled kSave subop %d", argv[0].toUint16());
}
- return s->r_acc;
+ return NULL_REG;
}
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index d587790b6c..e1e92b1cf9 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -23,20 +23,21 @@
*
*/
+#include "common/system.h"
+
+#include "engines/util.h"
#include "graphics/cursorman.h"
-#include "graphics/video/avi_decoder.h"
#include "graphics/surface.h"
+#include "gui/message.h"
+
#include "sci/sci.h"
#include "sci/debug.h" // for g_debug_sleeptime_factor
#include "sci/resource.h"
-#include "sci/video/seq_decoder.h"
#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
#include "sci/graphics/animate.h"
#include "sci/graphics/cache.h"
#include "sci/graphics/compare.h"
@@ -44,15 +45,21 @@
#include "sci/graphics/cursor.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/paint16.h"
+#include "sci/graphics/picture.h"
#include "sci/graphics/ports.h"
+#include "sci/graphics/robot.h"
#include "sci/graphics/screen.h"
+#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
+#ifdef ENABLE_SCI32
+#include "sci/graphics/frameout.h"
+#endif
namespace Sci {
void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) {
- GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view));
- uint16 signal = GET_SEL32V(s->_segMan, object, SELECTOR(signal));
+ GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
+ uint16 signal = readSelectorValue(s->_segMan, object, SELECTOR(signal));
int16 loopNo;
int16 maxLoops;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
@@ -91,7 +98,7 @@ void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *arg
if ((loopNo > 1) && (maxLoops < 4))
return;
- PUT_SEL32V(s->_segMan, object, SELECTOR(loop), loopNo);
+ writeSelectorValue(s->_segMan, object, SELECTOR(loop), loopNo);
}
static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) {
@@ -125,9 +132,10 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
break;
case -1:
// TODO: Special case at least in kq6, check disassembly
+ // Does something with magCursor, which is set on argc = 10, which we don't support
break;
case -2:
- // TODO: Special case at least in kq6, check disassembly
+ g_sci->_gfxCursor->kernelResetMoveZone();
break;
default:
g_sci->_gfxCursor->kernelShow();
@@ -141,10 +149,22 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxCursor->kernelSetPos(pos);
break;
case 4: {
- int16 top = argv[0].toSint16();
- int16 left = argv[1].toSint16();
- int16 bottom = argv[2].toSint16();
- int16 right = argv[3].toSint16();
+ int16 top, left, bottom, right;
+
+ if (getSciVersion() >= SCI_VERSION_2) {
+ top = argv[1].toSint16();
+ left = argv[0].toSint16();
+ bottom = argv[3].toSint16();
+ right = argv[2].toSint16();
+ } else {
+ top = argv[0].toSint16();
+ left = argv[1].toSint16();
+ bottom = argv[2].toSint16();
+ right = argv[3].toSint16();
+ }
+ // bottom/right needs to be included into our movezone, because we compare it like any regular Common::Rect
+ bottom++;
+ right++;
if ((right >= left) && (bottom >= top)) {
Common::Rect rect = Common::Rect(left, top, right, bottom);
@@ -154,8 +174,8 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
}
break;
}
+ case 9: // case for kq5cd, we are getting calling with 4 additional 900d parameters
case 5:
- case 9:
hotspot = new Common::Point(argv[3].toSint16(), argv[4].toSint16());
// Fallthrough
case 3:
@@ -164,8 +184,20 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
else
g_sci->_gfxCursor->kernelSetView(argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), hotspot);
break;
+ case 10:
+ // Freddy pharkas, when using the whiskey glass to read the prescription (bug #3034973)
+ // magnifier support, disabled using argc == 1, argv == -1
+ warning("kSetCursor: unsupported magnifier");
+ // we just set the view cursor currently
+ g_sci->_gfxCursor->kernelSetView(argv[5].toUint16(), argv[6].toUint16(), argv[7].toUint16(), hotspot);
+ // argv[0] -> 1, 2, 4 -> maybe magnification multiplier
+ // argv[1-4] -> rect for magnification
+ // argv[5, 6, 7] -> view resource for cursor
+ // argv[8] -> picture resource for mag
+ // argv[9] -> color for magnifier replacement
+ break;
default :
- warning("kSetCursor: Unhandled case: %d arguments given", argc);
+ error("kSetCursor: Unhandled case: %d arguments given", argc);
break;
}
return s->r_acc;
@@ -178,7 +210,7 @@ reg_t kSetCursor(EngineState *s, int argc, reg_t *argv) {
case SCI_VERSION_1_1:
return kSetCursorSci11(s, argc, argv);
default:
- warning("Unknown SetCursor type");
+ error("Unknown SetCursor type");
return NULL_REG;
}
}
@@ -199,121 +231,108 @@ reg_t kPicNotValid(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, g_sci->_gfxScreen->kernelPicNotValid(newPicNotValid));
}
-Common::Rect kGraphCreateRect(int16 x, int16 y, int16 x1, int16 y1) {
+static Common::Rect getGraphRect(reg_t *argv) {
+ int16 x = argv[1].toSint16();
+ int16 y = argv[0].toSint16();
+ int16 x1 = argv[3].toSint16();
+ int16 y1 = argv[2].toSint16();
if (x > x1) SWAP(x, x1);
if (y > y1) SWAP(y, y1);
return Common::Rect(x, y, x1, y1);
}
-// Graph subfunctions
-enum {
- K_GRAPH_GET_COLORS_NR = 2,
- // 3 - SET PALETTE VIA RESOURCE
- K_GRAPH_DRAW_LINE = 4,
- // 5 - NOP
- // 6 - DRAW PATTERN
- K_GRAPH_SAVE_BOX = 7,
- K_GRAPH_RESTORE_BOX = 8,
- K_GRAPH_FILL_BOX_BACKGROUND = 9,
- K_GRAPH_FILL_BOX_FOREGROUND = 10,
- K_GRAPH_FILL_BOX_ANY = 11,
- K_GRAPH_UPDATE_BOX = 12,
- K_GRAPH_REDRAW_BOX = 13,
- K_GRAPH_ADJUST_PRIORITY = 14,
- K_GRAPH_SAVE_UPSCALEDHIRES_BOX = 15 // KQ6CD Windows version
-};
+static Common::Point getGraphPoint(reg_t *argv) {
+ int16 x = argv[1].toSint16();
+ int16 y = argv[0].toSint16();
+ return Common::Point(x, y);
+}
reg_t kGraph(EngineState *s, int argc, reg_t *argv) {
- int16 x = 0, y = 0, x1 = 0, y1 = 0;
- uint16 screenMask;
- int16 priority, control, color, colorMask;
- Common::Rect rect;
-
- if (argc >= 5) {
- x = argv[2].toSint16();
- y = argv[1].toSint16();
- x1 = argv[4].toSint16();
- y1 = argv[3].toSint16();
- }
-
- switch (argv[0].toSint16()) {
- case K_GRAPH_GET_COLORS_NR:
- if (g_sci->getResMan()->isAmiga32color())
- return make_reg(0, 32);
- return make_reg(0, !g_sci->getResMan()->isVGA() ? 16 : 256);
-
- case K_GRAPH_DRAW_LINE:
- priority = (argc > 6) ? argv[6].toSint16() : -1;
- control = (argc > 7) ? argv[7].toSint16() : -1;
- color = argv[5].toSint16();
-
- // TODO: Find out why we get >15 for color in EGA
- if (!g_sci->getResMan()->isVGA() && !g_sci->getResMan()->isAmiga32color())
- color &= 0x0F;
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- g_sci->_gfxPaint16->kernelGraphDrawLine(Common::Point(x, y), Common::Point(x1, y1), color, priority, control);
- break;
+reg_t kGraphGetColorCount(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isAmiga32color())
+ return make_reg(0, 32);
+ return make_reg(0, !g_sci->getResMan()->isVGA() ? 16 : 256);
+}
- case K_GRAPH_SAVE_BOX:
- rect = kGraphCreateRect(x, y, x1, y1);
- screenMask = (argc > 5) ? argv[5].toUint16() : 0;
- return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask);
+reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
+ int16 color = argv[4].toSint16();
+ int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
+ int16 control = (argc > 6) ? argv[6].toSint16() : -1;
- case K_GRAPH_RESTORE_BOX:
- // This may be called with a memoryhandle from SAVE_BOX or SAVE_UPSCALEDHIRES_BOX
- g_sci->_gfxPaint16->kernelGraphRestoreBox(argv[1]);
- break;
+ // TODO: Find out why we get >15 for color in EGA
+ if (!g_sci->getResMan()->isVGA() && !g_sci->getResMan()->isAmiga32color())
+ color &= 0x0F;
- case K_GRAPH_FILL_BOX_BACKGROUND:
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphFillBoxBackground(rect);
- break;
+ g_sci->_gfxPaint16->kernelGraphDrawLine(getGraphPoint(argv), getGraphPoint(argv + 2), color, priority, control);
+ return s->r_acc;
+}
- case K_GRAPH_FILL_BOX_FOREGROUND:
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphFillBoxForeground(rect);
- break;
+reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL;
+ return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask);
+}
- case K_GRAPH_FILL_BOX_ANY:
- priority = (argc > 7) ? argv[7].toSint16() : -1;
- control = (argc > 8) ? argv[8].toSint16() : -1;
- color = argv[6].toSint16();
- colorMask = argv[5].toUint16();
+reg_t kGraphRestoreBox(EngineState *s, int argc, reg_t *argv) {
+ // This may be called with a memoryhandle from SAVE_BOX or SAVE_UPSCALEDHIRES_BOX
+ g_sci->_gfxPaint16->kernelGraphRestoreBox(argv[0]);
+ return s->r_acc;
+}
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control);
- break;
+reg_t kGraphFillBoxBackground(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ g_sci->_gfxPaint16->kernelGraphFillBoxBackground(rect);
+ return s->r_acc;
+}
- case K_GRAPH_UPDATE_BOX: {
- rect = kGraphCreateRect(x, y, x1, y1);
- bool hiresMode = (argc > 6) ? true : false;
- // argc == 7 on upscaled hires
- g_sci->_gfxPaint16->kernelGraphUpdateBox(rect, hiresMode);
- break;
- }
+reg_t kGraphFillBoxForeground(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ g_sci->_gfxPaint16->kernelGraphFillBoxForeground(rect);
+ return s->r_acc;
+}
- case K_GRAPH_REDRAW_BOX:
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphRedrawBox(rect);
- break;
+reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ int16 colorMask = argv[4].toUint16();
+ int16 color = argv[5].toSint16();
+ int16 priority = (argc > 6) ? argv[6].toSint16() : -1;
+ int16 control = (argc > 7) ? argv[7].toSint16() : -1;
- case K_GRAPH_ADJUST_PRIORITY:
- // Seems to be only implemented for SCI0/SCI01 games
- debugC(2, kDebugLevelGraphics, "adjust_priority(%d, %d)", argv[1].toUint16(), argv[2].toUint16());
- g_sci->_gfxPorts->kernelGraphAdjustPriority(argv[1].toUint16(), argv[2].toUint16());
- break;
+ g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control);
+ return s->r_acc;
+}
- case K_GRAPH_SAVE_UPSCALEDHIRES_BOX:
- rect = kGraphCreateRect(x, y, x1, y1);
- return g_sci->_gfxPaint16->kernelGraphSaveUpscaledHiresBox(rect);
+reg_t kGraphUpdateBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ // argv[4] is the map (1 for visual, etc.)
+ // argc == 6 on upscaled hires
+ bool hiresMode = (argc > 5) ? true : false;
+ g_sci->_gfxPaint16->kernelGraphUpdateBox(rect, hiresMode);
+ return s->r_acc;
+}
- default:
- warning("Unsupported kGraph() operation %04x", argv[0].toSint16());
- }
+reg_t kGraphRedrawBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ g_sci->_gfxPaint16->kernelGraphRedrawBox(rect);
+ return s->r_acc;
+}
+// Seems to be only implemented for SCI0/SCI01 games
+reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxPorts->kernelGraphAdjustPriority(argv[0].toUint16(), argv[1].toUint16());
return s->r_acc;
}
+reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ return g_sci->_gfxPaint16->kernelGraphSaveUpscaledHiresBox(rect);
+}
+
reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
int16 textWidth, textHeight;
Common::String text = s->_segMan->getString(argv[1]);
@@ -337,13 +356,14 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
}
textWidth = dest[3].toUint16(); textHeight = dest[2].toUint16();
-
+
#ifdef ENABLE_SCI32
- if (g_sci->_gui32)
- g_sci->_gui32->textSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
- else
+ if (!g_sci->_gfxText16) {
+ // TODO: Implement this
+ textWidth = 0; textHeight = 0;
+ } else
#endif
- g_sci->_gui->textSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
+ g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
debugC(2, kDebugLevelStrings, "GetTextSize '%s' -> %dx%d", text.c_str(), textWidth, textHeight);
dest[2] = make_reg(0, textHeight);
@@ -386,17 +406,16 @@ reg_t kCanBeHere(EngineState *s, int argc, reg_t *argv) {
reg_t curObject = argv[0];
reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
- bool canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
- return make_reg(0, canBeHere);
+ reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
+ return make_reg(0, canBeHere.isNull() ? 1 : 0);
}
-// kCantBeHere does the same thing as kCanBeHere, except that it returns the opposite result.
reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) {
reg_t curObject = argv[0];
reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
- bool canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
- return make_reg(0, !canBeHere);
+ reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
+ return canBeHere;
}
reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) {
@@ -437,7 +456,7 @@ reg_t kCelWide(EngineState *s, int argc, reg_t *argv) {
reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) {
reg_t object = argv[0];
- GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view));
+ GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
int16 loopCount;
loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId);
@@ -449,8 +468,8 @@ reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) {
reg_t kNumCels(EngineState *s, int argc, reg_t *argv) {
reg_t object = argv[0];
- GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view));
- int16 loopNo = GET_SEL32V(s->_segMan, object, SELECTOR(loop));
+ GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop));
int16 celCount;
celCount = g_sci->_gfxCache->kernelViewGetCelCount(viewId, loopNo);
@@ -522,14 +541,6 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
reg_t object = argv[0];
g_sci->_gfxCompare->kernelBaseSetter(object);
-
- // WORKAROUND for a problem in LSL1VGA. This allows the casino door to be opened,
- // till the actual problem is found
- if (s->_gameId == "lsl1sci" && s->currentRoomNumber() == 300) {
- int top = GET_SEL32V(s->_segMan, object, SELECTOR(brTop));
- PUT_SEL32V(s->_segMan, object, SELECTOR(brTop), top + 2);
- }
-
return s->r_acc;
}
@@ -539,64 +550,72 @@ reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+// we are called on EGA/amiga games as well, this doesnt make sense.
+// doing this would actually break the system EGA/amiga palette
reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
- // we are called on EGA/amiga games as well, this doesnt make sense.
- // doing this would actually break the system EGA/amiga palette
- if (!g_sci->getResMan()->isVGA())
- return s->r_acc;
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- switch (argv[0].toUint16()) {
- case 1: // Set resource palette
- if (argc==3) {
- GuiResourceId resourceId = argv[1].toUint16();
- bool force = argv[2].toUint16() == 2 ? true : false;
- g_sci->_gfxPalette->kernelSetFromResource(resourceId, force);
- } else {
- warning("kPalette(1) called with %d parameters", argc);
- }
- break;
- case 2: { // Set palette-flag(s)
- uint16 fromColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[2].toUint16(), 1, 255);
- uint16 flags = argv[3].toUint16();
+reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ GuiResourceId resourceId = argv[0].toUint16();
+ bool force = false;
+ if (argc == 2)
+ force = argv[1].toUint16() == 2 ? true : false;
+ g_sci->_gfxPalette->kernelSetFromResource(resourceId, force);
+ }
+ return s->r_acc;
+}
+
+reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 flags = argv[2].toUint16();
g_sci->_gfxPalette->kernelSetFlag(fromColor, toColor, flags);
- break;
}
- case 3: { // Remove palette-flag(s)
- uint16 fromColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[2].toUint16(), 1, 255);
- uint16 flags = argv[3].toUint16();
+ return s->r_acc;
+}
+
+reg_t kPaletteUnsetFlag(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 flags = argv[2].toUint16();
g_sci->_gfxPalette->kernelUnsetFlag(fromColor, toColor, flags);
- break;
}
- case 4: { // Set palette intensity
- switch (argc) {
- case 4:
- case 5: {
- uint16 fromColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[2].toUint16(), 1, 255);
- uint16 intensity = argv[3].toUint16();
- bool setPalette = (argc < 5) ? true : (argv[4].isNull()) ? true : false;
-
- g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette);
- break;
- }
- default:
- warning("kPalette(4) called with %d parameters", argc);
- }
- break;
+ return s->r_acc;
+}
+
+reg_t kPaletteSetIntensity(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 intensity = argv[2].toUint16();
+ bool setPalette = (argc < 4) ? true : (argv[3].isNull()) ? true : false;
+
+ g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette);
}
- case 5: { // Find closest color
- uint16 r = argv[1].toUint16();
- uint16 g = argv[2].toUint16();
- uint16 b = argv[3].toUint16();
+ return s->r_acc;
+}
+reg_t kPaletteFindColor(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 r = argv[0].toUint16();
+ uint16 g = argv[1].toUint16();
+ uint16 b = argv[2].toUint16();
return make_reg(0, g_sci->_gfxPalette->kernelFindColor(r, g, b));
}
- case 6: { // Animate
+ return NULL_REG;
+}
+
+reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
int16 argNr;
bool paletteChanged = false;
- for (argNr = 1; argNr < argc; argNr += 3) {
+ for (argNr = 0; argNr < argc; argNr += 3) {
uint16 fromColor = argv[argNr].toUint16();
uint16 toColor = argv[argNr + 1].toUint16();
int16 speed = argv[argNr + 2].toSint16();
@@ -605,80 +624,82 @@ reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
}
if (paletteChanged)
g_sci->_gfxPalette->kernelAnimateSet();
- break;
- }
- case 7: { // Save palette to heap
- warning("kPalette(7), save palette to heap STUB");
- break;
- }
- case 8: { // Restore palette from heap
- warning("kPalette(8), set stored palette STUB");
- break;
}
- default:
- warning("kPalette(%d), not implemented", argv[0].toUint16());
+ return s->r_acc;
+}
+
+reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ return g_sci->_gfxPalette->kernelSave();
}
+ return NULL_REG;
+}
- return s->r_acc;
+reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ g_sci->_gfxPalette->kernelRestore(argv[0]);
+ }
+ return argv[0];
}
-// This here is needed to make Pharkas work
reg_t kPalVary(EngineState *s, int argc, reg_t *argv) {
- uint16 operation = argv[0].toUint16();
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- if (!g_sci->_gui)
- return s->r_acc;
+reg_t kPalVaryInit(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[0].toUint16();
+ uint16 ticks = argv[1].toUint16();
+ uint16 stepStop = argc >= 3 ? argv[2].toUint16() : 64;
+ uint16 direction = argc >= 4 ? argv[3].toUint16() : 1;
+ if (g_sci->_gfxPalette->kernelPalVaryInit(paletteId, ticks, stepStop, direction))
+ return SIGNAL_REG;
+ return NULL_REG;
+}
- switch (operation) {
- case 0: { // Init
- GuiResourceId paletteId;
- uint16 time;
- if (argc == 3) {
- paletteId = argv[1].toUint16();
- time = argv[2].toUint16();
- g_sci->_gfxPalette->startPalVary(paletteId, time);
- warning("kPalVary(init) called with paletteId = %d, time = %d", paletteId, time);
- } else {
- warning("kPalVary(init) called with unsupported argc %d", argc);
- }
- break;
- }
- case 1: { // Unknown
- warning("kPalVary(1) called with parameter %d (argc %d)", argv[1].toUint16(), argc);
- break;
- }
- case 3: { // DeInit
- if (argc == 1) {
- g_sci->_gfxPalette->stopPalVary();
- warning("kPalVary(deinit)");
- } else {
- warning("kPalVary(deinit) called with unsupported argc %d", argc);
- }
- break;
- }
- case 4: { // Unknown
- warning("kPalVary(4) called with parameter %d (argc %d)", argv[1].toUint16(), argc);
- break;
- }
- case 6: { // Pause
- bool pauseState;
- if (argc == 2) {
- pauseState = argv[1].isNull() ? false : true;
- g_sci->_gfxPalette->togglePalVary(pauseState);
- warning("kPalVary(pause) called with state = %d", pauseState);
- } else {
- warning("kPalVary(pause) called with unsupported argc %d", argc);
- }
- break;
- }
- default:
- warning("kPalVary(%d), not implemented (argc = %d)", operation, argc);
- }
+reg_t kPalVaryReverse(EngineState *s, int argc, reg_t *argv) {
+ int16 ticks = argc >= 1 ? argv[0].toUint16() : -1;
+ int16 stepStop = argc >= 2 ? argv[1].toUint16() : 0;
+ int16 direction = argc >= 3 ? argv[2].toSint16() : -1;
+
+ return make_reg(0, g_sci->_gfxPalette->kernelPalVaryReverse(ticks, stepStop, direction));
+}
+
+reg_t kPalVaryGetCurrentStep(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_gfxPalette->kernelPalVaryGetCurrentStep());
+}
+
+reg_t kPalVaryDeinit(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxPalette->kernelPalVaryDeinit();
+ return NULL_REG;
+}
+
+reg_t kPalVaryChangeTarget(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[0].toUint16();
+ int16 currentStep = g_sci->_gfxPalette->kernelPalVaryChangeTarget(paletteId);
+ return make_reg(0, currentStep);
+}
+
+reg_t kPalVaryChangeTicks(EngineState *s, int argc, reg_t *argv) {
+ uint16 ticks = argv[0].toUint16();
+ g_sci->_gfxPalette->kernelPalVaryChangeTicks(ticks);
+ return NULL_REG;
+}
+
+reg_t kPalVaryPauseResume(EngineState *s, int argc, reg_t *argv) {
+ bool pauseState = !argv[0].isNull();
+ g_sci->_gfxPalette->kernelPalVaryPause(pauseState);
+ return NULL_REG;
+}
+
+reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv) {
+ // Unknown (seems to be SCI32 exclusive)
return NULL_REG;
}
reg_t kAssertPalette(EngineState *s, int argc, reg_t *argv) {
- GuiResourceId paletteId = argv[1].toUint16();
+ GuiResourceId paletteId = argv[0].toUint16();
g_sci->_gfxPalette->kernelAssertPalette(paletteId);
return s->r_acc;
@@ -692,9 +713,9 @@ reg_t kPortrait(EngineState *s, int argc, reg_t *argv) {
case 0: { // load
if (argc == 2) {
Common::String resourceName = s->_segMan->getString(argv[1]);
- s->r_acc = g_sci->_gui->portraitLoad(resourceName);
+ s->r_acc = g_sci->_gfxPaint16->kernelPortraitLoad(resourceName);
} else {
- warning("kPortrait(loadResource) called with unsupported argc %d", argc);
+ error("kPortrait(loadResource) called with unsupported argc %d", argc);
}
break;
}
@@ -709,31 +730,33 @@ reg_t kPortrait(EngineState *s, int argc, reg_t *argv) {
uint seq = argv[8].toUint16() & 0xff;
// argv[9] is usually 0??!!
- g_sci->_gui->portraitShow(resourceName, position, resourceNum, noun, verb, cond, seq);
+ g_sci->_gfxPaint16->kernelPortraitShow(resourceName, position, resourceNum, noun, verb, cond, seq);
return SIGNAL_REG;
} else {
- warning("kPortrait(show) called with unsupported argc %d", argc);
+ error("kPortrait(show) called with unsupported argc %d", argc);
}
break;
}
case 2: { // unload
if (argc == 2) {
uint16 portraitId = argv[1].toUint16();
- g_sci->_gui->portraitUnload(portraitId);
+ g_sci->_gfxPaint16->kernelPortraitUnload(portraitId);
} else {
- warning("kPortrait(unload) called with unsupported argc %d", argc);
+ error("kPortrait(unload) called with unsupported argc %d", argc);
}
break;
}
default:
- warning("kPortrait(%d), not implemented (argc = %d)", operation, argc);
+ error("kPortrait(%d), not implemented (argc = %d)", operation, argc);
}
return s->r_acc;
}
-// Original top-left must stay on kControl rects, we adjust accordingly because sierra sci actually wont draw rects that
-// are upside down (example: jones, when challenging jones - one button is a duplicate and also has lower-right which is 0, 0)
+// Original top-left must stay on kControl rects, we adjust accordingly because
+// sierra sci actually wont draw rects that are upside down (example: jones,
+// when challenging jones - one button is a duplicate and also has lower-right
+// which is 0, 0)
Common::Rect kControlCreateRect(int16 x, int16 y, int16 x1, int16 y1) {
if (x > x1) x1 = x;
if (y > y1) y1 = y;
@@ -741,12 +764,12 @@ Common::Rect kControlCreateRect(int16 x, int16 y, int16 x1, int16 y1) {
}
void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
- int16 type = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type));
- int16 style = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state));
- int16 x = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsLeft));
- int16 y = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsTop));
- GuiResourceId fontId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(font));
- reg_t textReference = GET_SEL32(s->_segMan, controlObject, SELECTOR(text));
+ int16 type = readSelectorValue(s->_segMan, controlObject, SELECTOR(type));
+ int16 style = readSelectorValue(s->_segMan, controlObject, SELECTOR(state));
+ int16 x = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsLeft));
+ int16 y = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsTop));
+ GuiResourceId fontId = readSelectorValue(s->_segMan, controlObject, SELECTOR(font));
+ reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
Common::String text;
Common::Rect rect;
TextAlignment alignment;
@@ -762,8 +785,8 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
bool isAlias = false;
rect = kControlCreateRect(x, y,
- GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsRight)),
- GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsBottom)));
+ readSelectorValue(s->_segMan, controlObject, SELECTOR(nsRight)),
+ readSelectorValue(s->_segMan, controlObject, SELECTOR(nsBottom)));
if (!textReference.isNull())
text = s->_segMan->getString(textReference);
@@ -775,32 +798,34 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
return;
case SCI_CONTROLS_TYPE_TEXT:
- alignment = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode));
+ alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
debugC(2, kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment);
g_sci->_gfxControls->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite);
return;
case SCI_CONTROLS_TYPE_TEXTEDIT:
- mode = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode));
- maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(max));
- cursorPos = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor));
+ mode = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
+ maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(max));
+ cursorPos = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor));
+ if (cursorPos > (int)text.size()) {
+ // if cursor is outside of text, adjust accordingly
+ cursorPos = text.size();
+ writeSelectorValue(s->_segMan, controlObject, SELECTOR(cursor), cursorPos);
+ }
debugC(2, kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y);
g_sci->_gfxControls->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite);
return;
case SCI_CONTROLS_TYPE_ICON:
- viewId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(view));
+ viewId = readSelectorValue(s->_segMan, controlObject, SELECTOR(view));
{
- int l = GET_SEL32V(s->_segMan, controlObject, SELECTOR(loop));
+ int l = readSelectorValue(s->_segMan, controlObject, SELECTOR(loop));
loopNo = (l & 0x80) ? l - 256 : l;
- int c = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cel));
+ int c = readSelectorValue(s->_segMan, controlObject, SELECTOR(cel));
celNo = (c & 0x80) ? c - 256 : c;
- // Game-specific: *ONLY* the jones EGA/VGA sierra interpreter contain code using priority selector
- // ALL other games use a hardcoded -1 (madness!)
- // We are detecting jones/talkie as "jones" as well, but the sierra interpreter of talkie doesnt have this
- // "hack". Hopefully it wont cause regressions (the code causes regressions if used against kq5/floppy)
- if (s->_gameId == "jones")
- priority = GET_SEL32V(s->_segMan, controlObject, SELECTOR(priority));
+ // Check if the control object specifies a priority selector (like in Jones)
+ if (lookupSelector(s->_segMan, controlObject, SELECTOR(priority), NULL, NULL) == kSelectorVariable)
+ priority = readSelectorValue(s->_segMan, controlObject, SELECTOR(priority));
else
priority = -1;
}
@@ -813,17 +838,17 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
if (type == SCI_CONTROLS_TYPE_LIST_ALIAS)
isAlias = true;
- maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry
- cursorOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor));
- if (g_sci->getKernel()->_selectorCache.topString != -1) {
+ maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry
+ cursorOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor));
+ if (SELECTOR(topString) != -1) {
// Games from early SCI1 onwards use topString
- upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(topString));
+ upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(topString));
} else {
// Earlier games use lsTop or brTop
- if (lookup_selector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable)
- upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(brTop));
+ if (lookupSelector(s->_segMan, controlObject, SELECTOR(brTop), NULL, NULL) == kSelectorVariable)
+ upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(brTop));
else
- upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(lsTop));
+ upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(lsTop));
}
// Count string entries in NULL terminated string list
@@ -873,9 +898,40 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
// Disable the "Change Directory" button, as we don't allow the game engine to
// change the directory where saved games are placed
- if (objName == "changeDirI") {
- int state = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state));
- PUT_SEL32V(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED);
+ // "changeDirItem" is used in the import windows of QFG2&3
+ if ((objName == "changeDirI") || (objName == "changeDirItem")) {
+ int state = readSelectorValue(s->_segMan, controlObject, SELECTOR(state));
+ writeSelectorValue(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED);
+ }
+ if (objName == "DEdit") {
+ reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
+ if (!textReference.isNull()) {
+ Common::String text = s->_segMan->getString(textReference);
+ if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav")) {
+ // Remove "a:" from hero quest / quest for glory export default filenames
+ text.deleteChar(0);
+ text.deleteChar(0);
+ s->_segMan->strcpy(textReference, text.c_str());
+ }
+ }
+ }
+ if (objName == "savedHeros") {
+ // Import of QfG character files dialog is shown
+ // display additional popup information before letting user use it
+ reg_t changeDirButton = s->_segMan->findObjectByName("changeDirItem");
+ if (!changeDirButton.isNull()) {
+ // check if checkDirButton is still enabled, in that case we are called the first time during that room
+ if (!(readSelectorValue(s->_segMan, changeDirButton, SELECTOR(state)) & SCI_CONTROLS_STYLE_DISABLED)) {
+ GUI::MessageDialog dialog("Characters saved inside ScummVM are shown "
+ "automatically. Character files saved in the original "
+ "interpreter need to be put inside ScummVM's saved games "
+ "directory and a prefix needs to be added depending on which "
+ "game it was saved in: 'qfg1-' for Quest for Glory 1, 'qfg2-' "
+ "for Quest for Glory 2. Example: 'qfg2-thief.sav'.",
+ "OK");
+ dialog.runModal();
+ }
+ }
}
_k_GenericDrawControl(s, controlObject, false);
@@ -894,7 +950,7 @@ reg_t kEditControl(EngineState *s, int argc, reg_t *argv) {
reg_t eventObject = argv[1];
if (!controlObject.isNull()) {
- int16 controlType = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type));
+ int16 controlType = readSelectorValue(s->_segMan, controlObject, SELECTOR(type));
switch (controlType) {
case SCI_CONTROLS_TYPE_TEXTEDIT:
@@ -954,14 +1010,13 @@ reg_t kSetPort(EngineState *s, int argc, reg_t *argv) {
case 7:
initPriorityBandsFlag = true;
- case 4:
case 6:
picRect.top = argv[0].toSint16();
picRect.left = argv[1].toSint16();
picRect.bottom = argv[2].toSint16();
picRect.right = argv[3].toSint16();
- picTop = (argc >= 6) ? argv[4].toSint16() : 0;
- picLeft = (argc >= 6) ? argv[5].toSint16() : 0;
+ picTop = argv[4].toSint16();
+ picLeft = argv[5].toSint16();
g_sci->_gfxPorts->kernelSetPicWindow(picRect, picTop, picLeft, initPriorityBandsFlag);
break;
@@ -980,28 +1035,26 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) {
uint16 y = argv[4].toUint16();
int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
uint16 paletteNo = (argc > 6) ? argv[6].toUint16() : 0;
- bool hiresMode = (argc > 7) ? true : false;
- reg_t upscaledHiresHandle = (argc > 7) ? argv[7] : NULL_REG;
-
- if ((s->_gameId == "freddypharkas") || (s->_gameId == "freddypharkas-demo")) {
- // WORKAROUND
- // Script 24 contains code that draws the game menu on screen. It uses a temp variable for setting priority that
- // is not set. in Sierra sci this happens to be 8250h. In our sci temporary variables are initialized thus we would
- // get 0 here resulting in broken menus.
- if ((viewId == 995) && (loopNo == 0) && (celNo == 0) && (priority == 0)) // game menu
- priority = 15;
- if ((viewId == 992) && (loopNo == 0) && (celNo == 0) && (priority == 0)) // quit game
- priority = 15;
- }
-
- if (s->_gameId == "laurabow2") {
- // WORKAROUND
- // see the one above
- if ((viewId == 995) && (priority == 0))
- priority = 15;
+ bool hiresMode = false;
+ reg_t upscaledHiresHandle = NULL_REG;
+ uint16 scaleX = 128;
+ uint16 scaleY = 128;
+
+ if (argc > 7) {
+ // this is either kq6 hires or scaling
+ if (paletteNo > 0) {
+ // it's scaling
+ scaleX = argv[6].toUint16();
+ scaleY = argv[7].toUint16();
+ paletteNo = 0;
+ } else {
+ // KQ6 hires
+ hiresMode = true;
+ upscaledHiresHandle = argv[7];
+ }
}
- g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, hiresMode, upscaledHiresHandle);
+ g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, scaleX, scaleY, hiresMode, upscaledHiresHandle);
return s->r_acc;
}
@@ -1043,10 +1096,6 @@ reg_t kAnimate(EngineState *s, int argc, reg_t *argv) {
reg_t castListReference = (argc > 0) ? argv[0] : NULL_REG;
bool cycle = (argc > 1) ? ((argv[1].toUint16()) ? true : false) : false;
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- // Take care of incoming events (kAnimate is called semi-regularly)
- process_sound_events(s);
-#endif
g_sci->_gfxAnimate->kernelAnimate(castListReference, cycle, argc, argv);
return s->r_acc;
@@ -1056,7 +1105,7 @@ reg_t kShakeScreen(EngineState *s, int argc, reg_t *argv) {
int16 shakeCount = (argc > 0) ? argv[0].toUint16() : 1;
int16 directions = (argc > 1) ? argv[1].toUint16() : 1;
- g_sci->_gfxPaint->kernelShakeScreen(shakeCount, directions);
+ g_sci->_gfxScreen->kernelShakeScreen(shakeCount, directions);
return s->r_acc;
}
@@ -1077,105 +1126,264 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) {
return g_sci->_gfxPaint16->kernelDisplay(g_sci->strSplit(text.c_str()).c_str(), argc, argv);
}
-reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
- // Hide the cursor if it's showing and then show it again if it was
- // previously visible.
- bool reshowCursor;
-
- reshowCursor = g_sci->_gfxCursor->isVisible();
- if (reshowCursor)
- g_sci->_gfxCursor->kernelHide();
-
- Graphics::VideoDecoder *videoDecoder = 0;
+reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) {
+ // This call is used for KQ6's intro. It has one parameter, which is 1 when
+ // the intro begins, and 0 when it ends. It is suspected that this is
+ // actually a flag to enable video planar memory access, as the video
+ // decoder in KQ6 is specifically written for the planar memory model.
+ // Planar memory mode access was used for VGA "Mode X" (320x240 resolution,
+ // although the intro in KQ6 is 320x200).
+ // Refer to http://en.wikipedia.org/wiki/Mode_X
- if (argv[0].segment != 0) {
- Common::String filename = s->_segMan->getString(argv[0]);
+ //warning("STUB: SetVideoMode %d", argv[0].toUint16());
+ return s->r_acc;
+}
- if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
- // Mac QuickTime
- // The only argument is the string for the video
- warning("TODO: Play QuickTime movie '%s'", filename.c_str());
- return s->r_acc;
- } else {
- // DOS SEQ
- // SEQ's are called with no subops, just the string and delay
- SeqDecoder *seqDecoder = new SeqDecoder();
- seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks
- videoDecoder = seqDecoder;
-
- if (!videoDecoder->loadFile(filename)) {
- warning("Failed to open movie file %s", filename.c_str());
- delete videoDecoder;
- videoDecoder = 0;
- }
- }
- } else {
- // Windows AVI (Macintosh QuickTime? Need to check KQ6 Macintosh)
- // TODO: This appears to be some sort of subop. case 0 contains the string
- // for the video, so we'll just play it from there for now.
+// New calls for SCI11. Using those is only needed when using text-codes so that
+// one is able to change font and/or color multiple times during kDisplay and
+// kDrawControl
+reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText16->kernelTextFonts(argc, argv);
+ return s->r_acc;
+}
+
+reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText16->kernelTextColors(argc, argv);
+ return s->r_acc;
+}
#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2_1) {
- // SCI2.1 always has argv[0] as 1, the rest of the arguments seem to
- // follow SCI1.1/2.
- if (argv[0].toUint16() != 1)
- error("SCI2.1 kShowMovie argv[0] not 1");
- argv++;
- argc--;
- }
-#endif
- switch (argv[0].toUint16()) {
- case 0: {
- Common::String filename = s->_segMan->getString(argv[1]);
- videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
-
- if (!videoDecoder->loadFile(filename.c_str())) {
- warning("Failed to open movie file %s", filename.c_str());
- delete videoDecoder;
- videoDecoder = 0;
- }
- break;
- }
- default:
- warning("Unhandled SCI kShowMovie subop %d", argv[1].toUint16());
- }
- }
- if (videoDecoder) {
- uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
- uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
+reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
+ // Returns 0 if the screen width or height is less than 640 or 400,
+ // respectively.
+ if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
+ return make_reg(0, 0);
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {
- if (videoDecoder->needsUpdate()) {
- Graphics::Surface *frame = videoDecoder->decodeNextFrame();
- if (frame) {
- g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+ return make_reg(0, 1);
+}
- if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+// SCI32 variant, can't work like sci16 variants
+reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
+// reg_t curObject = argv[0];
+// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
+
+ return NULL_REG;
+}
- g_system->updateScreen();
- }
- }
+reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
+ reg_t viewObj = argv[0];
- Common::Event event;
- while (g_system->getEventManager()->pollEvent(event))
- ;
+ g_sci->_gfxFrameout->kernelAddScreenItem(viewObj);
+ return NULL_REG;
+}
- g_system->delayMillis(10);
- }
-
- delete videoDecoder;
- g_sci->_gfxScreen->kernelSyncWithFramebuffer();
- }
+reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
+ //reg_t viewObj = argv[0];
+
+ //warning("kUpdateScreenItem, object %04x:%04x, view %d, loop %d, cel %d, pri %d", PRINT_REG(viewObj), viewId, loopNo, celNo, priority);
+ return NULL_REG;
+}
+
+reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
+ reg_t viewObj = argv[0];
+
+ g_sci->_gfxFrameout->kernelDeleteScreenItem(viewObj);
+
+ /*
+ reg_t viewObj = argv[0];
+ uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop));
+ int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel));
+ //int16 leftPos = 0;
+ //int16 topPos = 0;
+ int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority));
+ //int16 control = 0;
+ */
- if (reshowCursor)
- g_sci->_gfxCursor->kernelShow();
+ // TODO
+ //warning("kDeleteScreenItem, view %d, loop %d, cel %d, pri %d", viewId, loopNo, celNo, priority);
+ return NULL_REG;
+}
+
+reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+
+ g_sci->_gfxFrameout->kernelAddPlane(planeObj);
+ return NULL_REG;
+}
+
+reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+
+ g_sci->_gfxFrameout->kernelDeletePlane(planeObj);
+ return NULL_REG;
+}
+
+reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+
+ g_sci->_gfxFrameout->kernelUpdatePlane(planeObj);
return s->r_acc;
}
-#ifdef ENABLE_SCI32
+reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t picObj = argv[0];
+
+ // TODO
+
+ warning("kRepaintPlane object %04x:%04x", PRINT_REG(picObj));
+ return NULL_REG;
+}
+
+reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+ GuiResourceId pictureId = argv[1].toUint16();
+ int16 forWidth = argv[2].toSint16();
+ // argv[3] seems to be 0 most of the time
+
+ g_sci->_gfxFrameout->kernelAddPicAt(planeObj, forWidth, pictureId);
+ return s->r_acc;
+}
+
+reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri());
+}
+
+reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
+ // This kernel call likely seems to be doing the screen updates,
+ // as its called right after a view is updated
+
+ // TODO
+ g_sci->_gfxFrameout->kernelFrameout();
+
+ return NULL_REG;
+}
+
+reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
+ // Tests if the cursor is on the passed object
+
+ uint16 x = argv[0].toUint16();
+ uint16 y = argv[1].toUint16();
+ reg_t targetObject = argv[2];
+ uint16 illegalBits = argv[3].offset;
+ Common::Rect nsRect;
+
+ // we assume that x, y are local coordinates
+
+ // Get the bounding rectangle of the object
+ nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft));
+ nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
+ nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
+ nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
+
+ // nsRect top/left may be negative, adjust accordingly
+ Common::Rect checkRect = nsRect;
+ if (checkRect.top < 0)
+ checkRect.top = 0;
+ if (checkRect.left < 0)
+ checkRect.left = 0;
+
+ bool contained = checkRect.contains(x, y);
+ if (contained && illegalBits) {
+ // If illegalbits are set, we check the color of the pixel that got clicked on
+ // for now, we return false if the pixel is transparent
+ // although illegalBits may get differently set, don't know yet how this really works out
+ uint16 viewId = readSelectorValue(s->_segMan, targetObject, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(loop));
+ int16 celNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(cel));
+ if (g_sci->_gfxCompare->kernelIsItSkip(viewId, loopNo, celNo, Common::Point(x - nsRect.left, y - nsRect.top)))
+ contained = false;
+ }
+// these hacks shouldn't be needed anymore
+// uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
+// uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
+
+ // If top and left are negative, we need to adjust coordinates by
+ // the item's x and y (e.g. happens in GK1, day 1, with detective
+ // Mosely's hotspot in his office)
+
+// if (nsRect.left < 0)
+// nsRect.translate(itemX, 0);
+//
+// if (nsRect.top < 0)
+// nsRect.translate(0, itemY);
+
+// // HACK: nsLeft and nsTop can be invalid, so try and fix them here
+// // using x and y (e.g. with the inventory screen in GK1)
+// if (nsRect.left == itemY && nsRect.top == itemX) {
+// // Swap the values, as they're inversed (eh???)
+// nsRect.left = itemX;
+// nsRect.top = itemY;
+// }
+
+ return make_reg(0, contained);
+}
+
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
+ // Tests if the cursor is on the passed object, after adjusting the
+ // coordinates of the object according to the object's plane
+
+ uint16 x = argv[0].toUint16();
+ uint16 y = argv[1].toUint16();
+ reg_t targetObject = argv[2];
+ // TODO: argv[3] - it's usually 0
+ Common::Rect nsRect;
+
+ // Get the bounding rectangle of the object
+ nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft));
+ nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
+ nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
+ nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
+
+ // Get the object's plane
+#if 0
+ reg_t planeObject = readSelector(s->_segMan, targetObject, SELECTOR(plane));
+ if (!planeObject.isNull()) {
+ //uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
+ //uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
+ uint16 planeResY = readSelectorValue(s->_segMan, planeObject, SELECTOR(resY));
+ uint16 planeResX = readSelectorValue(s->_segMan, planeObject, SELECTOR(resX));
+ uint16 planeTop = readSelectorValue(s->_segMan, planeObject, SELECTOR(top));
+ uint16 planeLeft = readSelectorValue(s->_segMan, planeObject, SELECTOR(left));
+ planeTop = (planeTop * g_sci->_gfxScreen->getHeight()) / planeResY;
+ planeLeft = (planeLeft * g_sci->_gfxScreen->getWidth()) / planeResX;
+
+ // Adjust the bounding rectangle of the object by the object's
+ // actual X, Y coordinates
+ nsRect.top = ((nsRect.top * g_sci->_gfxScreen->getHeight()) / planeResY);
+ nsRect.left = ((nsRect.left * g_sci->_gfxScreen->getWidth()) / planeResX);
+ nsRect.bottom = ((nsRect.bottom * g_sci->_gfxScreen->getHeight()) / planeResY);
+ nsRect.right = ((nsRect.right * g_sci->_gfxScreen->getWidth()) / planeResX);
+
+ nsRect.translate(planeLeft, planeTop);
+ }
+#endif
+ //warning("kIsOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16());
+
+ return make_reg(0, nsRect.contains(x, y));
+}
+
+reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
+ // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1
+ switch (argv[0].toUint16()) {
+ case 0: {
+ if (argc != 4) {
+ warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
+ return NULL_REG;
+ }
+ reg_t object = argv[3];
+ Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ break;
+ }
+ default:
+ warning("CreateTextBitmap(%d)", argv[0].toUint16());
+ }
+
+ return NULL_REG;
+}
+
reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
int16 subop = argv[0].toUint16();
@@ -1188,8 +1396,15 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
int16 x = argv[4].toUint16();
int16 y = argv[5].toUint16();
warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y);
+ GfxRobot *test = new GfxRobot(g_sci->getResMan(), g_sci->_gfxScreen, id);
+ test->draw();
+ delete test;
+
}
break;
+ case 1: // LSL6 hires (startup)
+ // TODO
+ return NULL_REG; // an integer is expected
case 4: { // start
int id = argv[1].toUint16();
warning("kRobot(start), id %d", id);
@@ -1205,31 +1420,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-#endif
-
-reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) {
- // This call is used for KQ6's intro. It has one parameter, which is
- // 1 when the intro begins, and 0 when it ends. It is suspected that
- // this is actually a flag to enable video planar memory access, as
- // the video decoder in KQ6 is specifically written for the planar
- // memory model. Planar memory mode access was used for VGA "Mode X"
- // (320x240 resolution, although the intro in KQ6 is 320x200).
- // Refer to http://en.wikipedia.org/wiki/Mode_X
-
- //warning("STUB: SetVideoMode %d", argv[0].toUint16());
- return s->r_acc;
-}
-// New calls for SCI11. Using those is only needed when using text-codes so that one is able to change
-// font and/or color multiple times during kDisplay and kDrawControl
-reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gui->textFonts(argc, argv);
- return s->r_acc;
-}
-
-reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gui->textColors(argc, argv);
- return s->r_acc;
-}
+#endif
} // End of namespace Sci
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index c04454ca3d..93e95099f5 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -34,15 +34,20 @@ static bool isSaneNodePointer(SegManager *segMan, reg_t addr) {
reg_t prev = addr;
do {
- Node *node = segMan->lookupNode(addr);
+ Node *node = segMan->lookupNode(addr, false);
if (!node) {
- warning("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr));
+ if ((g_sci->getGameId() == GID_ICEMAN) && (g_sci->getEngineState()->currentRoomNumber() == 40)) {
+ // ICEMAN: when plotting course, unDrawLast is called by startPlot::changeState
+ // there is no previous entry so we get 0 in here
+ } else {
+ error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr));
+ }
return false;
}
if (havePrev && node->pred != prev) {
- warning("isSaneNodePointer: Node at %04x:%04x points to invalid predecessor %04x:%04x (should be %04x:%04x)",
+ error("isSaneNodePointer: Node at %04x:%04x points to invalid predecessor %04x:%04x (should be %04x:%04x)",
PRINT_REG(addr), PRINT_REG(node->pred), PRINT_REG(prev));
//node->pred = prev; // fix the problem in the node
@@ -58,33 +63,33 @@ static bool isSaneNodePointer(SegManager *segMan, reg_t addr) {
}
static void checkListPointer(SegManager *segMan, reg_t addr) {
- List *l = segMan->lookupList(addr);
+ List *list = segMan->lookupList(addr);
- if (!l) {
- warning("isSaneListPointer (list %04x:%04x): The requested list wasn't found",
+ if (!list) {
+ error("checkListPointer (list %04x:%04x): The requested list wasn't found",
PRINT_REG(addr));
return;
}
- if (l->first.isNull() && l->last.isNull()) {
+ if (list->first.isNull() && list->last.isNull()) {
// Empty list is fine
- } else if (!l->first.isNull() && !l->last.isNull()) {
+ } else if (!list->first.isNull() && !list->last.isNull()) {
// Normal list
- Node *node_a = segMan->lookupNode(l->first);
- Node *node_z = segMan->lookupNode(l->last);
+ Node *node_a = segMan->lookupNode(list->first, false);
+ Node *node_z = segMan->lookupNode(list->last, false);
if (!node_a) {
- warning("isSaneListPointer (list %04x:%04x): missing first node", PRINT_REG(addr));
+ error("checkListPointer (list %04x:%04x): missing first node", PRINT_REG(addr));
return;
}
if (!node_z) {
- warning("isSaneListPointer (list %04x:%04x): missing last node", PRINT_REG(addr));
+ error("checkListPointer (list %04x:%04x): missing last node", PRINT_REG(addr));
return;
}
if (!node_a->pred.isNull()) {
- warning("isSaneListPointer (list %04x:%04x): First node of the list points to a predecessor node",
+ error("checkListPointer (list %04x:%04x): First node of the list points to a predecessor node",
PRINT_REG(addr));
//node_a->pred = NULL_REG; // fix the problem in the node
@@ -93,7 +98,7 @@ static void checkListPointer(SegManager *segMan, reg_t addr) {
}
if (!node_z->succ.isNull()) {
- warning("isSaneListPointer (list %04x:%04x): Last node of the list points to a successor node",
+ error("checkListPointer (list %04x:%04x): Last node of the list points to a successor node",
PRINT_REG(addr));
//node_z->succ = NULL_REG; // fix the problem in the node
@@ -101,84 +106,42 @@ static void checkListPointer(SegManager *segMan, reg_t addr) {
return;
}
- isSaneNodePointer(segMan, l->first);
+ isSaneNodePointer(segMan, list->first);
} else {
// Not sane list... it's missing pointers to the first or last element
- if (l->first.isNull())
- warning("isSaneListPointer (list %04x:%04x): missing pointer to first element",
+ if (list->first.isNull())
+ error("checkListPointer (list %04x:%04x): missing pointer to first element",
PRINT_REG(addr));
- if (l->last.isNull())
- warning("isSaneListPointer (list %04x:%04x): missing pointer to last element",
+ if (list->last.isNull())
+ error("checkListPointer (list %04x:%04x): missing pointer to last element",
PRINT_REG(addr));
}
}
reg_t kNewList(EngineState *s, int argc, reg_t *argv) {
- reg_t listbase;
- List *l;
- l = s->_segMan->allocateList(&listbase);
- l->first = l->last = NULL_REG;
- debugC(2, kDebugLevelNodes, "New listbase at %04x:%04x", PRINT_REG(listbase));
+ reg_t listRef;
+ List *list = s->_segMan->allocateList(&listRef);
+ list->first = list->last = NULL_REG;
+ debugC(2, kDebugLevelNodes, "New listRef at %04x:%04x", PRINT_REG(listRef));
- return listbase; // Return list base address
+ return listRef; // Return list base address
}
reg_t kDisposeList(EngineState *s, int argc, reg_t *argv) {
// This function is not needed in ScummVM. The garbage collector
// cleans up unused objects automatically
-#if 0
- List *l = s->_segMan->lookupList(argv[0]);
-
- if (!l) {
- // FIXME: This should be an error, but it's turned to a warning for now
- warning("Attempt to dispose non-list at %04x:%04x", PRINT_REG(argv[0]));
- return NULL_REG;
- }
-
- checkListPointer(s->_segMan, argv[0]);
-
- if (!l->first.isNull()) {
- reg_t n_addr = l->first;
-
- while (!n_addr.isNull()) { // Free all nodes
- Node *n = s->_segMan->lookupNode(n_addr);
- n_addr = n->succ;
-
- //s->_segMan->free_Node(n_addr); // TODO
- }
- }
-
- //s->_segMan->free_list(argv[0]); // TODO
-#endif
-
return s->r_acc;
}
-static reg_t _k_new_node(EngineState *s, reg_t value, reg_t key) {
- reg_t nodebase;
- Node *n = s->_segMan->allocateNode(&nodebase);
-
- if (!n) {
- error("[Kernel] Out of memory while creating a node");
- return NULL_REG;
- }
-
- n->pred = n->succ = NULL_REG;
- n->key = key;
- n->value = value;
-
- return nodebase;
-}
-
reg_t kNewNode(EngineState *s, int argc, reg_t *argv) {
+ reg_t nodeValue = argv[0];
+ // Some SCI32 games call this with 1 parameter (e.g. the demo of Phantasmagoria).
+ // Set the key to be the same as the value in this case
+ reg_t nodeKey = (argc == 2) ? argv[1] : argv[0];
+ s->r_acc = s->_segMan->newNode(nodeValue, nodeKey);
- if (argc == 1)
- s->r_acc = _k_new_node(s, argv[0], argv[0]);
- else
- s->r_acc = _k_new_node(s, argv[0], argv[1]);
-
- debugC(2, kDebugLevelNodes, "New nodebase at %04x:%04x", PRINT_REG(s->r_acc));
+ debugC(2, kDebugLevelNodes, "New nodeRef at %04x:%04x", PRINT_REG(s->r_acc));
return s->r_acc;
}
@@ -187,11 +150,11 @@ reg_t kFirstNode(EngineState *s, int argc, reg_t *argv) {
if (argv[0].isNull())
return NULL_REG;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
- if (l) {
+ if (list) {
checkListPointer(s->_segMan, argv[0]);
- return l->first;
+ return list->first;
} else {
return NULL_REG;
}
@@ -201,11 +164,11 @@ reg_t kLastNode(EngineState *s, int argc, reg_t *argv) {
if (argv[0].isNull())
return NULL_REG;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
- if (l) {
+ if (list) {
checkListPointer(s->_segMan, argv[0]);
- return l->last;
+ return list->last;
} else {
return NULL_REG;
}
@@ -215,56 +178,56 @@ reg_t kEmptyList(EngineState *s, int argc, reg_t *argv) {
if (argv[0].isNull())
return NULL_REG;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
checkListPointer(s->_segMan, argv[0]);
- return make_reg(0, ((l) ? l->first.isNull() : 0));
+ return make_reg(0, ((list) ? list->first.isNull() : 0));
}
-static void _k_add_to_front(EngineState *s, reg_t listbase, reg_t nodebase) {
- List *l = s->_segMan->lookupList(listbase);
- Node *new_n = s->_segMan->lookupNode(nodebase);
+static void addToFront(EngineState *s, reg_t listRef, reg_t nodeRef) {
+ List *list = s->_segMan->lookupList(listRef);
+ Node *newNode = s->_segMan->lookupNode(nodeRef);
- debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
+ debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
- // FIXME: This should be an error, but it's turned to a warning for now
- if (!new_n)
- warning("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
- checkListPointer(s->_segMan, listbase);
+ if (!newNode)
+ error("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
+ checkListPointer(s->_segMan, listRef);
+
+ newNode->pred = NULL_REG;
+ newNode->succ = list->first;
- new_n->succ = l->first;
- new_n->pred = NULL_REG;
// Set node to be the first and last node if it's the only node of the list
- if (l->first.isNull())
- l->last = nodebase;
+ if (list->first.isNull())
+ list->last = nodeRef;
else {
- Node *old_n = s->_segMan->lookupNode(l->first);
- old_n->pred = nodebase;
+ Node *oldNode = s->_segMan->lookupNode(list->first);
+ oldNode->pred = nodeRef;
}
- l->first = nodebase;
+ list->first = nodeRef;
}
-static void _k_add_to_end(EngineState *s, reg_t listbase, reg_t nodebase) {
- List *l = s->_segMan->lookupList(listbase);
- Node *new_n = s->_segMan->lookupNode(nodebase);
+static void addToEnd(EngineState *s, reg_t listRef, reg_t nodeRef) {
+ List *list = s->_segMan->lookupList(listRef);
+ Node *newNode = s->_segMan->lookupNode(nodeRef);
+
+ debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
- debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
+ if (!newNode)
+ error("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
+ checkListPointer(s->_segMan, listRef);
- // FIXME: This should be an error, but it's turned to a warning for now
- if (!new_n)
- warning("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
- checkListPointer(s->_segMan, listbase);
+ newNode->pred = list->last;
+ newNode->succ = NULL_REG;
- new_n->succ = NULL_REG;
- new_n->pred = l->last;
// Set node to be the first and last node if it's the only node of the list
- if (l->last.isNull())
- l->first = nodebase;
+ if (list->last.isNull())
+ list->first = nodeRef;
else {
- Node *old_n = s->_segMan->lookupNode(l->last);
- old_n->succ = nodebase;
+ Node *old_n = s->_segMan->lookupNode(list->last);
+ old_n->succ = nodeRef;
}
- l->last = nodebase;
+ list->last = nodeRef;
}
reg_t kNextNode(EngineState *s, int argc, reg_t *argv) {
@@ -292,28 +255,43 @@ reg_t kNodeValue(EngineState *s, int argc, reg_t *argv) {
}
reg_t kAddToFront(EngineState *s, int argc, reg_t *argv) {
- _k_add_to_front(s, argv[0], argv[1]);
+ addToFront(s, argv[0], argv[1]);
+
+ if (argc == 3)
+ s->_segMan->lookupNode(argv[1])->key = argv[2];
+
+ return s->r_acc;
+}
+
+reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) {
+ addToEnd(s, argv[0], argv[1]);
+
+ if (argc == 3)
+ s->_segMan->lookupNode(argv[1])->key = argv[2];
+
return s->r_acc;
}
reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) {
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
Node *firstnode = argv[1].isNull() ? NULL : s->_segMan->lookupNode(argv[1]);
Node *newnode = s->_segMan->lookupNode(argv[2]);
checkListPointer(s->_segMan, argv[0]);
- // FIXME: This should be an error, but it's turned to a warning for now
if (!newnode) {
- warning("New 'node' %04x:%04x is not a node", PRINT_REG(argv[2]));
+ error("New 'node' %04x:%04x is not a node", PRINT_REG(argv[2]));
return NULL_REG;
}
- if (argc != 3) {
- warning("kAddAfter: Haven't got 3 arguments, aborting");
+ if (argc != 3 && argc != 4) {
+ error("kAddAfter: Haven't got 3 or 4 arguments, aborting");
return NULL_REG;
}
+ if (argc == 4)
+ newnode->key = argv[3];
+
if (firstnode) { // We're really appending after
reg_t oldnext = firstnode->succ;
@@ -323,22 +301,17 @@ reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) {
if (oldnext.isNull()) // Appended after last node?
// Set new node as last list node
- l->last = argv[2];
+ list->last = argv[2];
else
s->_segMan->lookupNode(oldnext)->pred = argv[2];
} else { // !firstnode
- _k_add_to_front(s, argv[0], argv[2]); // Set as initial list node
+ addToFront(s, argv[0], argv[2]); // Set as initial list node
}
return s->r_acc;
}
-reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) {
- _k_add_to_end(s, argv[0], argv[1]);
- return s->r_acc;
-}
-
reg_t kFindKey(EngineState *s, int argc, reg_t *argv) {
reg_t node_pos;
reg_t key = argv[1];
@@ -370,23 +343,27 @@ reg_t kFindKey(EngineState *s, int argc, reg_t *argv) {
reg_t kDeleteKey(EngineState *s, int argc, reg_t *argv) {
reg_t node_pos = kFindKey(s, 2, argv);
Node *n;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
if (node_pos.isNull())
return NULL_REG; // Signal failure
n = s->_segMan->lookupNode(node_pos);
- if (l->first == node_pos)
- l->first = n->succ;
- if (l->last == node_pos)
- l->last = n->pred;
+ if (list->first == node_pos)
+ list->first = n->succ;
+ if (list->last == node_pos)
+ list->last = n->pred;
if (!n->pred.isNull())
s->_segMan->lookupNode(n->pred)->succ = n->succ;
if (!n->succ.isNull())
s->_segMan->lookupNode(n->succ)->pred = n->pred;
- //s->_segMan->free_Node(node_pos); // TODO
+ // Erase references to the predecessor and successor nodes, as the game
+ // scripts could reference the node itself again.
+ // Happens in the intro of QFG1 and in Longbow, when exiting the cave.
+ n->pred = NULL_REG;
+ n->succ = NULL_REG;
return make_reg(0, 1); // Signal success
}
@@ -415,11 +392,9 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
reg_t dest = argv[1];
reg_t order_func = argv[2];
- int input_size = (int16)GET_SEL32V(segMan, source, SELECTOR(size));
- int i;
-
- reg_t input_data = GET_SEL32(segMan, source, SELECTOR(elements));
- reg_t output_data = GET_SEL32(segMan, dest, SELECTOR(elements));
+ int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size));
+ reg_t input_data = readSelector(segMan, source, SELECTOR(elements));
+ reg_t output_data = readSelector(segMan, dest, SELECTOR(elements));
List *list;
Node *node;
@@ -430,19 +405,20 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
if (output_data.isNull()) {
list = s->_segMan->allocateList(&output_data);
list->first = list->last = NULL_REG;
- PUT_SEL32(segMan, dest, SELECTOR(elements), output_data);
+ writeSelector(segMan, dest, SELECTOR(elements), output_data);
}
- PUT_SEL32V(segMan, dest, SELECTOR(size), input_size);
+ writeSelectorValue(segMan, dest, SELECTOR(size), input_size);
list = s->_segMan->lookupList(input_data);
node = s->_segMan->lookupNode(list->first);
sort_temp_t *temp_array = (sort_temp_t *)malloc(sizeof(sort_temp_t) * input_size);
- i = 0;
+ int i = 0;
while (node) {
- invoke_selector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value);
+ reg_t params[1] = { node->value };
+ invokeSelector(s, order_func, SELECTOR(doit), argc, argv, 1, params);
temp_array[i].key = node->key;
temp_array[i].value = node->value;
temp_array[i].order = s->r_acc;
@@ -453,8 +429,8 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp);
for (i = 0;i < input_size;i++) {
- reg_t lNode = _k_new_node(s, temp_array[i].key, temp_array[i].value);
- _k_add_to_end(s, output_data, lNode);
+ reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key);
+ addToEnd(s, output_data, lNode);
}
free(temp_array);
@@ -467,14 +443,14 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
reg_t kListAt(EngineState *s, int argc, reg_t *argv) {
if (argc != 2) {
- warning("kListAt called with %d parameters", argc);
+ error("kListAt called with %d parameters", argc);
return NULL_REG;
}
List *list = s->_segMan->lookupList(argv[0]);
reg_t curAddress = list->first;
if (list->first.isNull()) {
- warning("kListAt tried to reference empty list (%04x:%04x)", PRINT_REG(argv[0]));
+ error("kListAt tried to reference empty list (%04x:%04x)", PRINT_REG(argv[0]));
return NULL_REG;
}
Node *curNode = s->_segMan->lookupNode(curAddress);
@@ -533,15 +509,15 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) {
curObject = curNode->value;
// First, check if the target selector is a variable
- if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
+ if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// This can only happen with 3 params (list, target selector, variable)
if (argc != 3) {
- warning("kListEachElementDo: Attempted to modify a variable selector with %d params", argc);
+ error("kListEachElementDo: Attempted to modify a variable selector with %d params", argc);
} else {
- write_selector(s->_segMan, curObject, slc, argv[2]);
+ writeSelector(s->_segMan, curObject, slc, argv[2]);
}
} else {
- invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
}
curNode = s->_segMan->lookupNode(nextNode);
@@ -566,11 +542,11 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) {
curObject = curNode->value;
// First, check if the target selector is a variable
- if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
+ if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// Can this happen with variable selectors?
- warning("kListFirstTrue: Attempted to access a variable selector");
+ error("kListFirstTrue: Attempted to access a variable selector");
} else {
- invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
// Check if the result is true
if (!s->r_acc.isNull())
@@ -600,11 +576,11 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
curObject = curNode->value;
// First, check if the target selector is a variable
- if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
+ if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// Can this happen with variable selectors?
- warning("kListAllTrue: Attempted to access a variable selector");
+ error("kListAllTrue: Attempted to access a variable selector");
} else {
- invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
// Check if the result isn't true
if (s->r_acc.isNull())
@@ -617,65 +593,134 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-// In SCI2.1, all the list functions were merged in one
reg_t kList(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+reg_t kAddBefore(EngineState *s, int argc, reg_t *argv) {
+ error("Unimplemented function kAddBefore called");
+ return s->r_acc;
+}
+
+reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv) {
+ error("Unimplemented function kMoveToFront called");
+ return s->r_acc;
+}
+
+reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv) {
+ error("Unimplemented function kMoveToEnd called");
+ return s->r_acc;
+}
+
+reg_t kArray(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
- case 0:
- return kNewList(s, argc - 1, argv + 1);
- case 1:
- return kDisposeList(s, argc - 1, argv + 1);
- case 2:
- return kNewNode(s, argc - 1, argv + 1);
- case 3:
- return kFirstNode(s, argc - 1, argv + 1);
- case 4:
- return kLastNode(s, argc - 1, argv + 1);
- case 5:
- return kEmptyList(s, argc - 1, argv + 1);
- case 6:
- return kNextNode(s, argc - 1, argv + 1);
- case 7:
- return kPrevNode(s, argc - 1, argv + 1);
- case 8:
- return kNodeValue(s, argc - 1, argv + 1);
- case 9:
- return kAddAfter(s, argc - 1, argv + 1);
- case 10:
- return kAddToFront(s, argc - 1, argv + 1);
- case 11:
- return kAddToEnd(s, argc - 1, argv + 1);
- case 12:
- warning("kList: unimplemented subfunction kAddBefore");
- //return kAddBefore(s, argc - 1, argv + 1);
- return NULL_REG;
- case 13:
- warning("kList: unimplemented subfunction kMoveToFront");
- //return kMoveToFront(s, argc - 1, argv + 1);
- return NULL_REG;
- case 14:
- warning("kList: unimplemented subfunction kMoveToEnd");
- //return kMoveToEnd(s, argc - 1, argv + 1);
- return NULL_REG;
- case 15:
- return kFindKey(s, argc - 1, argv + 1);
- case 16:
- return kDeleteKey(s, argc - 1, argv + 1);
- case 17:
- return kListAt(s, argc - 1, argv + 1);
- case 18:
- return kListIndexOf(s, argc - 1, argv + 1);
- case 19:
- return kListEachElementDo(s, argc - 1, argv + 1);
- case 20:
- return kListFirstTrue(s, argc - 1, argv + 1);
- case 21:
- return kListAllTrue(s, argc - 1, argv + 1);
- case 22:
- return kSort(s, argc - 1, argv + 1);
+ case 0: { // New
+ reg_t arrayHandle;
+ SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle);
+ array->setType(argv[2].toUint16());
+ array->setSize(argv[1].toUint16());
+ return arrayHandle;
+ }
+ case 1: { // Size
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ return make_reg(0, array->getSize());
+ }
+ case 2: { // At (return value at an index)
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ return array->getValue(argv[2].toUint16());
+ }
+ case 3: { // Atput (put value at an index)
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+
+ uint32 index = argv[2].toUint16();
+ uint32 count = argc - 3;
+
+ if (index + count > 65535)
+ break;
+
+ if (array->getSize() < index + count)
+ array->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ array->setValue(i + index, argv[i + 3]);
+
+ return argv[1]; // We also have to return the handle
+ }
+ case 4: // Free
+ // Freeing of arrays is handled by the garbage collector
+ return s->r_acc;
+ case 5: { // Fill
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ uint16 index = argv[2].toUint16();
+
+ // A count of -1 means fill the rest of the array
+ uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16();
+ uint16 arraySize = array->getSize();
+
+ if (arraySize < index + count)
+ array->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ array->setValue(i + index, argv[4]);
+
+ return argv[1];
+ }
+ case 6: { // Cpy
+ if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY ||
+ s->_segMan->getSegmentObj(argv[3].segment)->getType() != SEG_TYPE_ARRAY) {
+ // Happens in the RAMA demo
+ warning("kArray(Cpy): Request to copy a segment which isn't an array, ignoring");
+ return NULL_REG;
+ }
+
+ SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
+ SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
+ uint32 index1 = argv[2].toUint16();
+ uint32 index2 = argv[4].toUint16();
+
+ // The original engine ignores bad copies too
+ if (index2 > array2->getSize())
+ break;
+
+ // A count of -1 means fill the rest of the array
+ uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16();
+
+ if (array1->getSize() < index1 + count)
+ array1->setSize(index1 + count);
+
+ for (uint16 i = 0; i < count; i++)
+ array1->setValue(i + index1, array2->getValue(i + index2));
+
+ return argv[1];
+ }
+ case 7: // Cmp
+ // Not implemented in SSCI
+ return s->r_acc;
+ case 8: { // Dup
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ reg_t arrayHandle;
+ SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
+
+ dupArray->setType(array->getType());
+ dupArray->setSize(array->getSize());
+
+ for (uint32 i = 0; i < array->getSize(); i++)
+ dupArray->setValue(i, array->getValue(i));
+
+ return arrayHandle;
+ }
+ case 9: // Getdata
+ if (!s->_segMan->isHeapObject(argv[1]))
+ return argv[1];
+
+ return readSelector(s->_segMan, argv[1], SELECTOR(data));
default:
- warning("kList: Unhandled case %d", argv[0].toUint16());
- return NULL_REG;
+ error("Unknown kArray subop %d", argv[0].toUint16());
}
+
+ return NULL_REG;
}
#endif
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index dbf317860f..f3769b653b 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -29,16 +29,40 @@
namespace Sci {
reg_t kRandom(EngineState *s, int argc, reg_t *argv) {
- int fromNumber = argv[0].toUint16();
- int toNumber = argv[1].toUint16();
- double randomNumber = fromNumber + ((toNumber + 1.0 - fromNumber) * (rand() / (RAND_MAX + 1.0)));
- return make_reg(0, (int)randomNumber);
+ switch (argc) {
+ case 1: // set seed to argv[0]
+ // SCI0/SCI01 just reset the seed to 0 instead of using argv[0] at all
+ return NULL_REG;
+
+ case 2: { // get random number
+ int fromNumber = argv[0].toUint16();
+ int toNumber = argv[1].toUint16();
+
+ // TODO/CHECKME: It is propbably not required to check whether
+ // toNumber is greater than fromNumber, at least not when one
+ // goes by their names, but let us be on the safe side and
+ // allow toNumber to be smaller than fromNumber too.
+ if (fromNumber > toNumber)
+ SWAP(fromNumber, toNumber);
+
+ const uint diff = (uint)(toNumber - fromNumber);
+
+ const int randomNumber = fromNumber + (int)g_sci->getRNG().getRandomNumber(diff);
+ return make_reg(0, randomNumber);
+ }
+
+ case 3: // get seed
+ // SCI0/01 did not support this at all
+ // Actually we would have to return the previous seed
+ error("kRandom: scripts asked for previous seed");
+ break;
+
+ default:
+ error("kRandom: unsupported argc");
+ }
}
reg_t kAbs(EngineState *s, int argc, reg_t *argv) {
- // This is a hack, but so is the code in Hoyle1 that needs it.
- if (argv[0].segment)
- return make_reg(0, 0x3e9); // Yes people, this is an object
return make_reg(0, abs(argv[0].toSint16()));
}
@@ -112,7 +136,7 @@ reg_t kCosDiv(EngineState *s, int argc, reg_t *argv) {
double cosval = cos(angle * PI / 180.0);
if ((cosval < 0.0001) && (cosval > -0.0001)) {
- warning("kCosDiv: Attempted division by zero");
+ error("kCosDiv: Attempted division by zero");
return SIGNAL_REG;
} else
return make_reg(0, (int16)(value / cosval));
@@ -124,7 +148,7 @@ reg_t kSinDiv(EngineState *s, int argc, reg_t *argv) {
double sinval = sin(angle * PI / 180.0);
if ((sinval < 0.0001) && (sinval > -0.0001)) {
- warning("kSinDiv: Attempted division by zero");
+ error("kSinDiv: Attempted division by zero");
return SIGNAL_REG;
} else
return make_reg(0, (int16)(value / sinval));
@@ -136,7 +160,7 @@ reg_t kTimesTan(EngineState *s, int argc, reg_t *argv) {
param -= 90;
if ((param % 90) == 0) {
- warning("kTimesTan: Attempted tan(pi/2)");
+ error("kTimesTan: Attempted tan(pi/2)");
return SIGNAL_REG;
} else
return make_reg(0, (int16) - (tan(param * PI / 180.0) * scale));
@@ -147,10 +171,28 @@ reg_t kTimesCot(EngineState *s, int argc, reg_t *argv) {
int scale = (argc > 1) ? argv[1].toSint16() : 1;
if ((param % 90) == 0) {
- warning("kTimesCot: Attempted tan(pi/2)");
+ error("kTimesCot: Attempted tan(pi/2)");
return SIGNAL_REG;
} else
return make_reg(0, (int16)(tan(param * PI / 180.0) * scale));
}
+#ifdef ENABLE_SCI32
+
+reg_t kMulDiv(EngineState *s, int argc, reg_t *argv) {
+ int16 multiplicant = argv[0].toSint16();
+ int16 multiplier = argv[1].toSint16();
+ int16 denominator = argv[2].toSint16();
+
+ // Sanity check...
+ if (!denominator) {
+ error("kMulDiv: attempt to divide by zero (%d * %d / %d", multiplicant, multiplier, denominator);
+ return NULL_REG;
+ }
+
+ return make_reg(0, multiplicant * multiplier / denominator);
+}
+
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kmenu.cpp b/engines/sci/engine/kmenu.cpp
index a5f2f01297..c8a6e03556 100644
--- a/engines/sci/engine/kmenu.cpp
+++ b/engines/sci/engine/kmenu.cpp
@@ -27,7 +27,6 @@
#include "sci/resource.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/menu.h"
@@ -74,7 +73,7 @@ reg_t kDrawStatus(EngineState *s, int argc, reg_t *argv) {
int16 colorBack = (argc > 2) ? argv[2].toSint16() : g_sci->getResMan()->isVGA() ? 255 : 15;
if (!textReference.isNull()) {
- // Sometimes this is called without giving text, if thats the case dont process it
+ // Sometimes this is called without giving text, if thats the case dont process it.
text = s->_segMan->getString(textReference);
g_sci->_gfxMenu->kernelDrawStatus(g_sci->strSplit(text.c_str(), NULL).c_str(), colorPen, colorBack);
@@ -91,10 +90,9 @@ reg_t kDrawMenuBar(EngineState *s, int argc, reg_t *argv) {
reg_t kMenuSelect(EngineState *s, int argc, reg_t *argv) {
reg_t eventObject = argv[0];
- //bool pauseSound = argc > 1 ? (argv[1].isNull() ? false : true) : false;
+ bool pauseSound = argc > 1 ? (argv[1].isNull() ? false : true) : true;
- // TODO: pauseSound implementation
- return g_sci->_gfxMenu->kernelSelect(eventObject);
+ return g_sci->_gfxMenu->kernelSelect(eventObject, pauseSound);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index 450dca3770..fbe20410de 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -31,17 +31,14 @@
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/gc.h"
-#include "sci/graphics/gui.h"
+#include "sci/graphics/maciconbar.h"
namespace Sci {
reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) {
- s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW;
- s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; // This appears to help
+ s->shrinkStackToBase();
- shrink_execution_stack(s, s->execution_stack_base + 1);
-
- script_abort_flag = 1; // Force vm to abort ASAP
+ s->abortScriptProcessing = kAbortRestartGame; // Force vm to abort ASAP
return NULL_REG;
}
@@ -49,47 +46,40 @@ reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) {
** Returns the restarting_flag in acc
*/
reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {
- s->r_acc = make_reg(0, (s->restarting_flags & SCI_GAME_WAS_RESTARTED));
+ s->r_acc = make_reg(0, s->gameIsRestarting);
if (argc) { // Only happens during replay
if (!argv[0].toUint16()) // Set restarting flag
- s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED;
+ s->gameIsRestarting = GAMEISRESTARTING_NONE;
}
uint32 neededSleep = 30;
- // WORKAROUND:
- // LSL3 calculates a machinespeed variable during game startup (right after the filthy questions)
- // This one would go through w/o throttling resulting in having to do 1000 pushups or something
- // Another way of handling this would be delaying incrementing of "machineSpeed" selector
- if (s->_gameId == "lsl3" && s->currentRoomNumber() == 290)
- s->_throttleTrigger = true;
- if (s->_gameId == "iceman" && s->currentRoomNumber() == 27) {
- s->_throttleTrigger = true;
- neededSleep = 60;
- }
-
- if (s->_throttleTrigger) {
- // Some games seem to get the duration of main loop initially and then switch of animations for the whole game
- // based on that (qfg2, iceman). We are now running full speed initially to avoid that.
- // It seems like we dont need to do that anymore
- //if (s->_throttleCounter < 50) {
- // s->_throttleCounter++;
- // return s->r_acc;
- //}
-
- uint32 curTime = g_system->getMillis();
- uint32 duration = curTime - s->_throttleLastTime;
-
- if (duration < neededSleep) {
- s->_event->sleep(neededSleep - duration);
- s->_throttleLastTime = g_system->getMillis();
- } else {
- s->_throttleLastTime = curTime;
+ // WORKAROUNDS:
+ switch (g_sci->getGameId()) {
+ case GID_LSL3:
+ // LSL3 calculates a machinespeed variable during game startup
+ // (right after the filthy questions). This one would go through w/o
+ // throttling resulting in having to do 1000 pushups or something. Another
+ // way of handling this would be delaying incrementing of "machineSpeed"
+ // selector.
+ if (s->currentRoomNumber() == 290)
+ s->_throttleTrigger = true;
+ break;
+ case GID_ICEMAN:
+ // In ICEMAN the submarine control room is not animating much, so it runs way too fast
+ // we calm it down even more otherwise especially fighting against other submarines
+ // is almost impossible
+ if (s->currentRoomNumber() == 27) {
+ s->_throttleTrigger = true;
+ neededSleep = 60;
}
- s->_throttleTrigger = false;
+ break;
+ default:
+ break;
}
+ s->speedThrottler(neededSleep);
return s->r_acc;
}
@@ -106,7 +96,11 @@ enum kMemoryInfoFunc {
};
reg_t kMemoryInfo(EngineState *s, int argc, reg_t *argv) {
- const uint16 size = 0x7fff; // Must not be 0xffff, or some memory calculations will overflow
+ // The free heap size returned must not be 0xffff, or some memory
+ // calculations will overflow. Crazy Nick's games handle up to 32746
+ // bytes (0x7fea), otherwise they throw a warning that the memory is
+ // fragmented
+ const uint16 size = 0x7fea;
switch (argv[0].offset) {
case K_MEMORYINFO_LARGEST_HEAP_BLOCK:
@@ -120,7 +114,7 @@ reg_t kMemoryInfo(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, size);
default:
- warning("Unknown MemoryInfo operation: %04x", argv[0].offset);
+ error("Unknown MemoryInfo operation: %04x", argv[0].offset);
}
return NULL_REG;
@@ -172,16 +166,16 @@ reg_t kFlushResources(EngineState *s, int argc, reg_t *argv) {
reg_t kSetDebug(EngineState *s, int argc, reg_t *argv) {
printf("Debug mode activated\n");
- g_debugState.seeking = kDebugSeekNothing;
- g_debugState.runningStep = 0;
+ g_sci->_debugState.seeking = kDebugSeekNothing;
+ g_sci->_debugState.runningStep = 0;
return s->r_acc;
}
enum {
- K_NEW_GETTIME_TICKS = 0,
- K_NEW_GETTIME_TIME_12HOUR = 1,
- K_NEW_GETTIME_TIME_24HOUR = 2,
- K_NEW_GETTIME_DATE = 3
+ KGETTIME_TICKS = 0,
+ KGETTIME_TIME_12HOUR = 1,
+ KGETTIME_TIME_24HOUR = 2,
+ KGETTIME_DATE = 3
};
reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
@@ -190,32 +184,32 @@ reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
int retval = 0; // Avoid spurious warning
g_system->getTimeAndDate(loc_time);
- elapsedTime = g_system->getMillis() - s->game_start_time;
+ elapsedTime = g_system->getMillis() - s->gameStartTime;
int mode = (argc > 0) ? argv[0].toUint16() : 0;
if (getSciVersion() <= SCI_VERSION_0_LATE && mode > 1)
- warning("kGetTime called in SCI0 with mode %d (expected 0 or 1)", mode);
+ error("kGetTime called in SCI0 with mode %d (expected 0 or 1)", mode);
switch (mode) {
- case K_NEW_GETTIME_TICKS :
+ case KGETTIME_TICKS :
retval = elapsedTime * 60 / 1000;
debugC(2, kDebugLevelTime, "GetTime(elapsed) returns %d", retval);
break;
- case K_NEW_GETTIME_TIME_12HOUR :
+ case KGETTIME_TIME_12HOUR :
retval = ((loc_time.tm_hour % 12) << 12) | (loc_time.tm_min << 6) | (loc_time.tm_sec);
debugC(2, kDebugLevelTime, "GetTime(12h) returns %d", retval);
break;
- case K_NEW_GETTIME_TIME_24HOUR :
+ case KGETTIME_TIME_24HOUR :
retval = (loc_time.tm_hour << 11) | (loc_time.tm_min << 5) | (loc_time.tm_sec >> 1);
debugC(2, kDebugLevelTime, "GetTime(24h) returns %d", retval);
break;
- case K_NEW_GETTIME_DATE :
+ case KGETTIME_DATE :
retval = loc_time.tm_mday | ((loc_time.tm_mon + 1) << 5) | (((loc_time.tm_year + 1900) & 0x7f) << 9);
debugC(2, kDebugLevelTime, "GetTime(date) returns %d", retval);
break;
default:
- warning("Attempt to use unknown GetTime mode %d", mode);
+ error("Attempt to use unknown GetTime mode %d", mode);
break;
}
@@ -233,17 +227,32 @@ enum {
reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
- case K_MEMORY_ALLOCATE_CRITICAL :
- if (!s->_segMan->allocDynmem(argv[1].toUint16(), "kMemory() critical", &s->r_acc)) {
+ case K_MEMORY_ALLOCATE_CRITICAL: {
+ int byteCount = argv[1].toUint16();
+ // WORKAROUND: pq3 (multilingual) when plotting crimes - allocates the
+ // returned bytes from kStrLen on "W" and "E" and wants to put a
+ // string in there, which doesn't fit of course. That's why we allocate
+ // one byte more all the time inside that room
+ if (g_sci->getGameId() == GID_PQ3) {
+ if (s->currentRoomNumber() == 202)
+ byteCount++;
+ }
+ if (!s->_segMan->allocDynmem(byteCount, "kMemory() critical", &s->r_acc)) {
error("Critical heap allocation failed");
}
break;
- case K_MEMORY_ALLOCATE_NONCRITICAL :
+ }
+ case K_MEMORY_ALLOCATE_NONCRITICAL:
s->_segMan->allocDynmem(argv[1].toUint16(), "kMemory() non-critical", &s->r_acc);
break;
case K_MEMORY_FREE :
- if (s->_segMan->freeDynmem(argv[1])) {
- error("Attempt to kMemory::free() non-dynmem pointer %04x:%04x", PRINT_REG(argv[1]));
+ if (!s->_segMan->freeDynmem(argv[1])) {
+ if (g_sci->getGameId() == GID_QFG1VGA) {
+ // Ignore script bug in QFG1VGA, when closing any conversation dialog with esc
+ } else {
+ // Usually, the result of a script bug. Non-critical
+ warning("Attempt to kMemory::free() non-dynmem pointer %04x:%04x", PRINT_REG(argv[1]));
+ }
}
break;
case K_MEMORY_MEMCPY : {
@@ -252,11 +261,16 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
break;
}
case K_MEMORY_PEEK : {
+ if (!argv[1].segment) {
+ // This occurs in KQ5CD when interacting with certain objects
+ warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
+ return s->r_acc;
+ }
+
SegmentRef ref = s->_segMan->dereference(argv[1]);
if (!ref.isValid() || ref.maxSize < 2) {
- // This occurs in KQ5CD when interacting with certain objects
- warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
+ error("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
return s->r_acc;
}
if (ref.isRaw)
@@ -272,7 +286,7 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
SegmentRef ref = s->_segMan->dereference(argv[1]);
if (!ref.isValid() || ref.maxSize < 2) {
- warning("Attempt to poke invalid memory at %04x:%04x", PRINT_REG(argv[1]));
+ error("Attempt to poke invalid memory at %04x:%04x", PRINT_REG(argv[1]));
return s->r_acc;
}
@@ -298,9 +312,12 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
reg_t kIconBar(EngineState *s, int argc, reg_t *argv) {
// TODO...
- if (argv[0].toUint16() == 4 && argv[1].toUint16() == 0)
+ if (argv[0].toUint16() == 4 && argv[1].toUint16() == 0) {
for (int i = 0; i < argv[2].toUint16(); i++)
- warning("kIconBar: Icon Object %d = %04x:%04x", i, PRINT_REG(argv[i + 3]));
+ g_sci->_gfxMacIconBar->addIcon(argv[i + 3]);
+
+ g_sci->_gfxMacIconBar->drawIcons();
+ }
// Other calls seem to handle selecting/deselecting them
@@ -327,11 +344,12 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
bool isWindows = g_sci->getPlatform() == Common::kPlatformWindows;
if (argc == 0 && getSciVersion() < SCI_VERSION_2) {
- // This is called in KQ5CD with no parameters, where it seems to do some graphics
- // driver check. This kernel function didn't have subfunctions then. If 0 is
- // returned, the game functions normally, otherwise all the animations show up
- // like a slideshow (e.g. in the intro). So we return 0. However, the behavior
- // changed for kPlatform with no parameters in SCI32.
+ // This is called in KQ5CD with no parameters, where it seems to do some
+ // graphics driver check. This kernel function didn't have subfunctions
+ // then. If 0 is returned, the game functions normally, otherwise all
+ // the animations show up like a slideshow (e.g. in the intro). So we
+ // return 0. However, the behavior changed for kPlatform with no
+ // parameters in SCI32.
return NULL_REG;
}
@@ -363,10 +381,53 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
case kPlatformIsItWindows:
return make_reg(0, isWindows);
default:
- warning("Unsupported kPlatform operation %d", operation);
+ error("Unsupported kPlatform operation %d", operation);
+ }
+
+ return NULL_REG;
+}
+
+reg_t kEmpty(EngineState *s, int argc, reg_t *argv) {
+ // Placeholder for empty kernel functions which are still called from the
+ // engine scripts (like the empty kSetSynonyms function in SCI1.1). This
+ // differs from dummy functions because it does nothing and never throws a
+ // warning when it is called.
+ return s->r_acc;
+}
+
+reg_t kStub(EngineState *s, int argc, reg_t *argv) {
+ Kernel *kernel = g_sci->getKernel();
+ int kernelCallNr = -1;
+
+ Common::List<ExecStack>::iterator callIterator = s->_executionStack.end();
+ if (callIterator != s->_executionStack.begin()) {
+ callIterator--;
+ ExecStack lastCall = *callIterator;
+ kernelCallNr = lastCall.debugSelector;
}
+ Common::String warningMsg = "Dummy function k" + kernel->getKernelName(kernelCallNr) +
+ Common::String::printf("[%x]", kernelCallNr) +
+ " invoked. Params: " +
+ Common::String::printf("%d", argc) + " (";
+
+ for (int i = 0; i < argc; i++) {
+ warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += (i == argc - 1 ? ")" : ", ");
+ }
+
+ warning("%s", warningMsg.c_str());
+ return s->r_acc;
+}
+
+reg_t kStubNull(EngineState *s, int argc, reg_t *argv) {
+ kStub(s, argc, argv);
return NULL_REG;
}
+reg_t kDummy(EngineState *s, int argc, reg_t *argv) {
+ kStub(s, argc, argv);
+ error("Kernel function was called, which was considered to be unused - see log for details");
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index fcaf0d7ea0..5c23539bf8 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -30,45 +30,47 @@
#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
#include "sci/graphics/animate.h"
+#include "sci/graphics/screen.h"
namespace Sci {
-/*
-Compute "velocity" vector (xStep,yStep)=(vx,vy) for a jump from (0,0) to (dx,dy), with gravity gy.
-The gravity is assumed to be non-negative.
-
-If this was ordinary continuous physics, we would compute the desired (floating point!)
-velocity vector (vx,vy) as follows, under the assumption that vx and vy are linearly correlated
-by some constant factor c, i.e. vy = c * vx:
- dx = t * vx
- dy = t * vy + gy * t^2 / 2
-=> dy = c * dx + gy * (dx/vx)^2 / 2
-=> |vx| = sqrt( gy * dx^2 / (2 * (dy - c * dx)) )
-Here, the sign of vx must be chosen equal to the sign of dx, obviously.
-
-Clearly, this square root only makes sense in our context if the denominator is positive,
-or equivalently, (dy - c * dx) must be positive. For simplicity and by symmetry
-along the x-axis, we assume dx to be positive for all computations, and only adjust for
-its sign in the end. Switching the sign of c appropriately, we set tmp := (dy + c * dx)
-and compute c so that this term becomes positive.
-
-Remark #1: If the jump is straight up, i.e. dx == 0, then we should not assume the above
-linear correlation vy = c * vx of the velocities (as vx will be 0, but vy shouldn't be,
-unless we drop).
-
-
-Remark #2: We are actually in a discrete setup. The motion is computed iteratively: each iteration,
-we add vx and vy to the position, then add gy to vy. So the real formula is the following
-(where t is ideally close to an int):
-
- dx = t * vx
- dy = t * vy + gy * t*(t-1) / 2
-
-But the solution resulting from that is a lot more complicated, so we use the above approximation instead.
-
-Still, what we compute in the end is of course not a real velocity anymore, but an integer approximation,
-used in an iterative stepping algorithm
-*/
+/**
+ * Compute "velocity" vector (xStep,yStep)=(vx,vy) for a jump from (0,0) to
+ * (dx,dy), with gravity constant gy. The gravity is assumed to be non-negative.
+ *
+ * If this was ordinary continuous physics, we would compute the desired
+ * (floating point!) velocity vector (vx,vy) as follows, under the assumption
+ * that vx and vy are linearly correlated by a constant c, i.e., vy = c * vx:
+ * dx = t * vx
+ * dy = t * vy + gy * t^2 / 2
+ * => dy = c * dx + gy * (dx/vx)^2 / 2
+ * => |vx| = sqrt( gy * dx^2 / (2 * (dy - c * dx)) )
+ * Here, the sign of vx must be chosen equal to the sign of dx, obviously.
+ *
+ * This square root only makes sense in our context if the denominator is
+ * positive, or equivalently, (dy - c * dx) must be positive. For simplicity
+ * and by symmetry along the x-axis, we assume dx to be positive for all
+ * computations, and only adjust for its sign in the end. Switching the sign of
+ * c appropriately, we set tmp := (dy + c * dx) and compute c so that this term
+ * becomes positive.
+ *
+ * Remark #1: If the jump is straight up, i.e. dx == 0, then we should not
+ * assume the above linear correlation vy = c * vx of the velocities (as vx
+ * will be 0, but vy shouldn't be, unless we drop down).
+ *
+ * Remark #2: We are actually in a discrete setup. The motion is computed
+ * iteratively: each iteration, we add vx and vy to the position, then add gy
+ * to vy. So the real formula is the following (where t ideally is close to an int):
+ *
+ * dx = t * vx
+ * dy = t * vy + gy * t*(t-1) / 2
+ *
+ * But the solution resulting from that is a lot more complicated, so we use
+ * the above approximation instead.
+ *
+ * Still, what we compute in the end is of course not a real velocity anymore,
+ * but an integer approximation, used in an iterative stepping algorithm.
+ */
reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
// Input data
@@ -115,7 +117,7 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
//tmp = dx * 3 / 2; // ALMOST the resulting value, except for obvious rounding issues
// FIXME: Where is the 3 coming from? Maybe they hard/coded, by "accident", that usually gy=3 ?
- // Then this choice of will make t equal to roughly sqrt(dx)
+ // Then this choice of scalar will make t equal to roughly sqrt(dx)
}
}
// POST: c >= 1
@@ -158,8 +160,8 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "SetJump for object at %04x:%04x", PRINT_REG(object));
debugC(2, kDebugLevelBresen, "xStep: %d, yStep: %d", vx, vy);
- PUT_SEL32V(segMan, object, SELECTOR(xStep), vx);
- PUT_SEL32V(segMan, object, SELECTOR(yStep), vy);
+ writeSelectorValue(segMan, object, SELECTOR(xStep), vx);
+ writeSelectorValue(segMan, object, SELECTOR(yStep), vy);
return s->r_acc;
}
@@ -168,9 +170,9 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
#define _K_BRESEN_AXIS_Y 1
static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t mover, int step_factor, int deltax, int deltay) {
- reg_t client = GET_SEL32(segMan, mover, SELECTOR(client));
- int stepx = (int16)GET_SEL32V(segMan, client, SELECTOR(xStep)) * step_factor;
- int stepy = (int16)GET_SEL32V(segMan, client, SELECTOR(yStep)) * step_factor;
+ reg_t client = readSelector(segMan, mover, SELECTOR(client));
+ int stepx = (int16)readSelectorValue(segMan, client, SELECTOR(xStep)) * step_factor;
+ int stepy = (int16)readSelectorValue(segMan, client, SELECTOR(yStep)) * step_factor;
int numsteps_x = stepx ? (abs(deltax) + stepx - 1) / stepx : 0;
int numsteps_y = stepy ? (abs(deltay) + stepy - 1) / stepy : 0;
int bdi, i1;
@@ -191,15 +193,15 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m
/* if (abs(deltax) > abs(deltay)) {*/ // Bresenham on y
if (numsteps_y < numsteps_x) {
- PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y);
- PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y);
+ writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1);
//i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step);
//bdi = -abs(deltax);
i1 = 2 * (abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step);
bdi = -abs(deltax);
} else { // Bresenham on x
- PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X);
- PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X);
+ writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1);
//i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step);
//bdi = -abs(deltay);
i1 = 2 * (abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step);
@@ -207,26 +209,26 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m
}
- PUT_SEL32V(segMan, mover, SELECTOR(dx), deltax_step);
- PUT_SEL32V(segMan, mover, SELECTOR(dy), deltay_step);
+ writeSelectorValue(segMan, mover, SELECTOR(dx), deltax_step);
+ writeSelectorValue(segMan, mover, SELECTOR(dy), deltay_step);
debugC(2, kDebugLevelBresen, "Init bresen for mover %04x:%04x: d=(%d,%d)", PRINT_REG(mover), deltax, deltay);
debugC(2, kDebugLevelBresen, " steps=%d, mv=(%d, %d), i1= %d, i2=%d",
numsteps, deltax_step, deltay_step, i1, bdi*2);
- //PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre?
- PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi);
- PUT_SEL32V(segMan, mover, SELECTOR(b_i1), i1);
- PUT_SEL32V(segMan, mover, SELECTOR(b_i2), bdi * 2);
+ //writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre?
+ writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi);
+ writeSelectorValue(segMan, mover, SELECTOR(b_i1), i1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_i2), bdi * 2);
}
reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t mover = argv[0];
- reg_t client = GET_SEL32(segMan, mover, SELECTOR(client));
+ reg_t client = readSelector(segMan, mover, SELECTOR(client));
- int deltax = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)) - (int16)GET_SEL32V(segMan, client, SELECTOR(x));
- int deltay = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)) - (int16)GET_SEL32V(segMan, client, SELECTOR(y));
+ int deltax = (int16)readSelectorValue(segMan, mover, SELECTOR(x)) - (int16)readSelectorValue(segMan, client, SELECTOR(x));
+ int deltay = (int16)readSelectorValue(segMan, mover, SELECTOR(y)) - (int16)readSelectorValue(segMan, client, SELECTOR(y));
int step_factor = (argc < 1) ? argv[1].toUint16() : 1;
initialize_bresen(s->_segMan, argc, argv, mover, step_factor, deltax, deltay);
@@ -240,42 +242,51 @@ reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) {
reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t mover = argv[0];
- reg_t client = GET_SEL32(segMan, mover, SELECTOR(client));
+ reg_t client = readSelector(segMan, mover, SELECTOR(client));
- int x = (int16)GET_SEL32V(segMan, client, SELECTOR(x));
- int y = (int16)GET_SEL32V(segMan, client, SELECTOR(y));
+ int x = (int16)readSelectorValue(segMan, client, SELECTOR(x));
+ int y = (int16)readSelectorValue(segMan, client, SELECTOR(y));
int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis;
- uint16 signal = GET_SEL32V(segMan, client, SELECTOR(signal));
+ uint16 signal = readSelectorValue(segMan, client, SELECTOR(signal));
int completed = 0;
- int max_movcnt = GET_SEL32V(segMan, client, SELECTOR(moveSpeed));
+ int max_movcnt = readSelectorValue(segMan, client, SELECTOR(moveSpeed));
if (getSciVersion() > SCI_VERSION_01)
signal &= ~kSignalHitObstacle;
- PUT_SEL32(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0
+ writeSelector(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0
oldx = x;
oldy = y;
- destx = (int16)GET_SEL32V(segMan, mover, SELECTOR(x));
- desty = (int16)GET_SEL32V(segMan, mover, SELECTOR(y));
- dx = (int16)GET_SEL32V(segMan, mover, SELECTOR(dx));
- dy = (int16)GET_SEL32V(segMan, mover, SELECTOR(dy));
- bdi = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_di));
- bi1 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i1));
- bi2 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i2));
- movcnt = GET_SEL32V(segMan, mover, SELECTOR(b_movCnt));
- bdelta = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_incr));
- axis = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_xAxis));
+ destx = (int16)readSelectorValue(segMan, mover, SELECTOR(x));
+ desty = (int16)readSelectorValue(segMan, mover, SELECTOR(y));
+ dx = (int16)readSelectorValue(segMan, mover, SELECTOR(dx));
+ dy = (int16)readSelectorValue(segMan, mover, SELECTOR(dy));
+ bdi = (int16)readSelectorValue(segMan, mover, SELECTOR(b_di));
+ bi1 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i1));
+ bi2 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i2));
+ movcnt = readSelectorValue(segMan, mover, SELECTOR(b_movCnt));
+ bdelta = (int16)readSelectorValue(segMan, mover, SELECTOR(b_incr));
+ axis = (int16)readSelectorValue(segMan, mover, SELECTOR(b_xAxis));
+
+ if ((getSciVersion() >= SCI_VERSION_1_LATE)) {
+ // Mixed-Up Fairy Tales has no xLast/yLast selectors
+ if (SELECTOR(xLast) != -1) {
+ // save last position into mover
+ writeSelectorValue(segMan, mover, SELECTOR(xLast), x);
+ writeSelectorValue(segMan, mover, SELECTOR(yLast), y);
+ }
+ }
//printf("movecnt %d, move speed %d\n", movcnt, max_movcnt);
if (g_sci->_features->handleMoveCount()) {
if (max_movcnt > movcnt) {
++movcnt;
- PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
return NULL_REG;
} else {
movcnt = 0;
- PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
}
}
@@ -288,7 +299,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
dy += bdelta;
}
- PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi);
+ writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi);
x += dx;
y += dy;
@@ -303,6 +314,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
|| ((y == desty) && (abs(dy) >= abs(dx))) /* Moving fast, reached? */
))) {
// Whew... in short: If we have reached or passed our target position
+
x = destx;
y = desty;
completed = 1;
@@ -310,34 +322,46 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x", PRINT_REG(mover));
}
- PUT_SEL32V(segMan, client, SELECTOR(x), x);
- PUT_SEL32V(segMan, client, SELECTOR(y), y);
+ writeSelectorValue(segMan, client, SELECTOR(x), x);
+ writeSelectorValue(segMan, client, SELECTOR(y), y);
debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi);
- if (g_sci->getKernel()->_selectorCache.cantBeHere != -1) {
- invoke_selector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0);
- s->r_acc = make_reg(0, !s->r_acc.offset);
+ bool collision = false;
+ reg_t cantBeHere = NULL_REG;
+
+ if (SELECTOR(cantBeHere) != -1) {
+ // adding this here for hoyle 3 to get happy. CantBeHere is a dummy in hoyle 3 and acc is != 0 so we would
+ // get a collision otherwise
+ s->r_acc = NULL_REG;
+ invokeSelector(s, client, SELECTOR(cantBeHere), argc, argv);
+ if (!s->r_acc.isNull())
+ collision = true;
+ cantBeHere = s->r_acc;
} else {
- invoke_selector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0);
+ invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
+ if (s->r_acc.isNull())
+ collision = true;
}
- if (!s->r_acc.offset) { // Contains the return value
- signal = GET_SEL32V(segMan, client, SELECTOR(signal));
+ if (collision) {
+ signal = readSelectorValue(segMan, client, SELECTOR(signal));
- PUT_SEL32V(segMan, client, SELECTOR(x), oldx);
- PUT_SEL32V(segMan, client, SELECTOR(y), oldy);
- PUT_SEL32V(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle));
+ writeSelectorValue(segMan, client, SELECTOR(x), oldx);
+ writeSelectorValue(segMan, client, SELECTOR(y), oldy);
+ writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle));
debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover));
- completed = 1;
+ // We shall not set completed in this case, sierra sci also doesn't do it
+ // if we set call .moveDone in those cases qfg1 vga gate at the castle and lsl1 casino door will not work
}
- // FIXME: find out why iceman needs this and we ask for version > SCI01
- if ((getSciVersion() > SCI_VERSION_01) || (s->_gameId == "iceman"))
+ if ((getSciVersion() >= SCI_VERSION_1_EGA))
if (completed)
- invoke_selector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0);
+ invokeSelector(s, mover, SELECTOR(moveDone), argc, argv);
+ if (SELECTOR(cantBeHere) != -1)
+ return cantBeHere;
return make_reg(0, completed);
}
@@ -374,59 +398,52 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
s->r_acc = SIGNAL_REG;
if (!s->_segMan->isHeapObject(avoider)) {
- warning("DoAvoider() where avoider %04x:%04x is not an object", PRINT_REG(avoider));
+ error("DoAvoider() where avoider %04x:%04x is not an object", PRINT_REG(avoider));
return NULL_REG;
}
- client = GET_SEL32(segMan, avoider, SELECTOR(client));
+ client = readSelector(segMan, avoider, SELECTOR(client));
if (!s->_segMan->isHeapObject(client)) {
- warning("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client));
+ error("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client));
return NULL_REG;
}
- looper = GET_SEL32(segMan, client, SELECTOR(looper));
- mover = GET_SEL32(segMan, client, SELECTOR(mover));
+ looper = readSelector(segMan, client, SELECTOR(looper));
+ mover = readSelector(segMan, client, SELECTOR(mover));
if (!s->_segMan->isHeapObject(mover)) {
if (mover.segment) {
- warning("DoAvoider() where mover %04x:%04x is not an object", PRINT_REG(mover));
+ error("DoAvoider() where mover %04x:%04x is not an object", PRINT_REG(mover));
}
return s->r_acc;
}
- destx = GET_SEL32V(segMan, mover, SELECTOR(x));
- desty = GET_SEL32V(segMan, mover, SELECTOR(y));
+ destx = readSelectorValue(segMan, mover, SELECTOR(x));
+ desty = readSelectorValue(segMan, mover, SELECTOR(y));
debugC(2, kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty);
- if (invoke_selector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) {
- error("Mover %04x:%04x of avoider %04x:%04x doesn't have a doit() funcselector", PRINT_REG(mover), PRINT_REG(avoider));
- return NULL_REG;
- }
+ invokeSelector(s, mover, SELECTOR(doit), argc, argv);
- mover = GET_SEL32(segMan, client, SELECTOR(mover));
+ mover = readSelector(segMan, client, SELECTOR(mover));
if (!mover.segment) // Mover has been disposed?
return s->r_acc; // Return gracefully.
- if (invoke_selector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) {
- error("Client %04x:%04x of avoider %04x:%04x doesn't"
- " have an isBlocked() funcselector", PRINT_REG(client), PRINT_REG(avoider));
- return NULL_REG;
- }
+ invokeSelector(s, client, SELECTOR(isBlocked), argc, argv);
- dx = destx - GET_SEL32V(segMan, client, SELECTOR(x));
- dy = desty - GET_SEL32V(segMan, client, SELECTOR(y));
+ dx = destx - readSelectorValue(segMan, client, SELECTOR(x));
+ dy = desty - readSelectorValue(segMan, client, SELECTOR(y));
angle = getAngle(dx, dy);
debugC(2, kDebugLevelBresen, "Movement (%d,%d), angle %d is %sblocked", dx, dy, angle, (s->r_acc.offset) ? " " : "not ");
if (s->r_acc.offset) { // isBlocked() returned non-zero
- int rotation = (rand() & 1) ? 45 : (360 - 45); // Clockwise/counterclockwise
- int oldx = GET_SEL32V(segMan, client, SELECTOR(x));
- int oldy = GET_SEL32V(segMan, client, SELECTOR(y));
- int xstep = GET_SEL32V(segMan, client, SELECTOR(xStep));
- int ystep = GET_SEL32V(segMan, client, SELECTOR(yStep));
+ int rotation = (g_sci->getRNG().getRandomBit() == 1) ? 45 : (360 - 45); // Clockwise/counterclockwise
+ int oldx = readSelectorValue(segMan, client, SELECTOR(x));
+ int oldy = readSelectorValue(segMan, client, SELECTOR(y));
+ int xstep = readSelectorValue(segMan, client, SELECTOR(xStep));
+ int ystep = readSelectorValue(segMan, client, SELECTOR(yStep));
int moves;
debugC(2, kDebugLevelBresen, " avoider %04x:%04x", PRINT_REG(avoider));
@@ -435,23 +452,19 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
int move_x = (int)(sin(angle * PI / 180.0) * (xstep));
int move_y = (int)(-cos(angle * PI / 180.0) * (ystep));
- PUT_SEL32V(segMan, client, SELECTOR(x), oldx + move_x);
- PUT_SEL32V(segMan, client, SELECTOR(y), oldy + move_y);
+ writeSelectorValue(segMan, client, SELECTOR(x), oldx + move_x);
+ writeSelectorValue(segMan, client, SELECTOR(y), oldy + move_y);
debugC(2, kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y);
- if (invoke_selector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) {
- error("Client %04x:%04x of avoider %04x:%04x doesn't"
- " have a canBeHere() funcselector", PRINT_REG(client), PRINT_REG(avoider));
- return NULL_REG;
- }
+ invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
- PUT_SEL32V(segMan, client, SELECTOR(x), oldx);
- PUT_SEL32V(segMan, client, SELECTOR(y), oldy);
+ writeSelectorValue(segMan, client, SELECTOR(x), oldx);
+ writeSelectorValue(segMan, client, SELECTOR(y), oldy);
if (s->r_acc.offset) { // We can be here
debugC(2, kDebugLevelBresen, "Success");
- PUT_SEL32V(segMan, client, SELECTOR(heading), angle);
+ writeSelectorValue(segMan, client, SELECTOR(heading), angle);
return make_reg(0, angle);
}
@@ -462,23 +475,22 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
angle -= 360;
}
- warning("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider));
+ error("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider));
} else {
- int heading = GET_SEL32V(segMan, client, SELECTOR(heading));
+ int heading = readSelectorValue(segMan, client, SELECTOR(heading));
if (heading == -1)
return s->r_acc; // No change
- PUT_SEL32V(segMan, client, SELECTOR(heading), angle);
+ writeSelectorValue(segMan, client, SELECTOR(heading), angle);
s->r_acc = make_reg(0, angle);
+ reg_t params[2] = { make_reg(0, angle), client };
+
if (looper.segment) {
- if (invoke_selector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) {
- error("Looper %04x:%04x of avoider %04x:%04x doesn't"
- " have a doit() funcselector", PRINT_REG(looper), PRINT_REG(avoider));
- } else
- return s->r_acc;
+ invokeSelector(s, looper, SELECTOR(doit), argc, argv, 2, params);
+ return s->r_acc;
} else {
// No looper? Fall back to DirLoop
_k_dirloop(client, (uint16)angle, s, argc, argv);
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index 0254d21642..552e425906 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -31,6 +31,8 @@
#include "sci/engine/message.h"
#include "sci/engine/kernel.h"
+//#define DEBUG_PARSER
+
namespace Sci {
/*************************************************************/
@@ -42,6 +44,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
reg_t heap_said_block = argv[0];
byte *said_block;
int new_lastmatch;
+ Vocabulary *voc = g_sci->getVocabulary();
#ifdef DEBUG_PARSER
const int debug_parser = 1;
#else
@@ -59,11 +62,11 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
}
#ifdef DEBUG_PARSER
- debugC(2, kDebugLevelParser, "Said block:", 0);
- s->_voc->decipherSaidBlock(said_block);
+ printf("Said block: ");
+ g_sci->getVocabulary()->debugDecipherSaidBlock(said_block);
#endif
- if (s->_voc->parser_event.isNull() || (GET_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed)))) {
+ if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) {
return NULL_REG;
}
@@ -77,7 +80,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, 1);
if (new_lastmatch != SAID_PARTIAL_MATCH)
- PUT_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed), 1);
+ writeSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed), 1);
} else {
return NULL_REG;
@@ -92,20 +95,21 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
char *error;
ResultWordList words;
reg_t event = argv[1];
- Vocabulary *voc = s->_voc;
-
- s->_voc->parser_event = event;
+ g_sci->checkVocabularySwitch();
+ Vocabulary *voc = g_sci->getVocabulary();
+ voc->parser_event = event;
+ reg_t params[2] = { voc->parser_base, stringpos };
bool res = voc->tokenizeString(words, string.c_str(), &error);
- s->_voc->parserIsValid = false; /* not valid */
+ voc->parserIsValid = false; /* not valid */
if (res && !words.empty()) {
- s->_voc->synonymizeTokens(words);
+ voc->synonymizeTokens(words);
s->r_acc = make_reg(0, 1);
#ifdef DEBUG_PARSER
- debugC(2, kDebugLevelParser, "Parsed to the following blocks:", 0);
+ debugC(2, kDebugLevelParser, "Parsed to the following blocks:");
for (ResultWordList::const_iterator i = words.begin(); i != words.end(); ++i)
debugC(2, kDebugLevelParser, " Type[%04x] Group[%04x]", i->_class, i->_group);
@@ -115,32 +119,32 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
if (syntax_fail) {
s->r_acc = make_reg(0, 1);
- PUT_SEL32V(segMan, event, SELECTOR(claimed), 1);
+ writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
- invoke_selector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos);
+ invokeSelector(s, g_sci->getGameObject(), SELECTOR(syntaxFail), argc, argv, 2, params);
/* Issue warning */
debugC(2, kDebugLevelParser, "Tree building failed");
} else {
- s->_voc->parserIsValid = true;
- PUT_SEL32V(segMan, event, SELECTOR(claimed), 0);
+ voc->parserIsValid = true;
+ writeSelectorValue(segMan, event, SELECTOR(claimed), 0);
#ifdef DEBUG_PARSER
- s->_voc->dumpParseTree();
+ voc->dumpParseTree();
#endif
}
} else {
s->r_acc = make_reg(0, 0);
- PUT_SEL32V(segMan, event, SELECTOR(claimed), 1);
+ writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
if (error) {
- s->_segMan->strcpy(s->_voc->parser_base, error);
+ s->_segMan->strcpy(voc->parser_base, error);
debugC(2, kDebugLevelParser, "Word unknown: %s", error);
/* Issue warning: */
- invoke_selector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos);
+ invokeSelector(s, g_sci->getGameObject(), SELECTOR(wordFail), argc, argv, 2, params);
free(error);
return make_reg(0, 1); /* Tell them that it didn't work */
}
@@ -156,28 +160,29 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
Node *node;
int script;
int numSynonyms = 0;
+ Vocabulary *voc = g_sci->getVocabulary();
// Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub
if (getSciVersion() > SCI_VERSION_1_EGA)
return s->r_acc;
- s->_voc->clearSynonyms();
+ voc->clearSynonyms();
- list = s->_segMan->lookupList(GET_SEL32(segMan, object, SELECTOR(elements)));
+ list = s->_segMan->lookupList(readSelector(segMan, object, SELECTOR(elements)));
node = s->_segMan->lookupNode(list->first);
while (node) {
reg_t objpos = node->value;
int seg;
- script = GET_SEL32V(segMan, objpos, SELECTOR(number));
+ script = readSelectorValue(segMan, objpos, SELECTOR(number));
seg = s->_segMan->getScriptSegment(script);
if (seg > 0)
numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr();
if (numSynonyms) {
- byte *synonyms = s->_segMan->getScript(seg)->getSynonyms();
+ const byte *synonyms = s->_segMan->getScript(seg)->getSynonyms();
if (synonyms) {
debugC(2, kDebugLevelParser, "Setting %d synonyms for script.%d",
@@ -193,7 +198,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
synonym_t tmp;
tmp.replaceant = (int16)READ_LE_UINT16(synonyms + i * 4);
tmp.replacement = (int16)READ_LE_UINT16(synonyms + i * 4 + 2);
- s->_voc->addSynonym(tmp);
+ voc->addSynonym(tmp);
}
} else
warning("Synonyms of script.%03d were requested, but script is not available", script);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 25d967c247..07d0a31f0b 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -265,7 +265,8 @@ struct PathfindingState {
static Common::Point read_point(SegManager *segMan, reg_t list, int offset) {
SegmentRef list_r = segMan->dereference(list);
if (!list_r.isValid() || list_r.skipByte) {
- warning("read_point(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(list));
+ // If this happens, then the code below will probably go OOB and crash
+ error("read_point(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(list));
}
Common::Point point;
@@ -337,15 +338,15 @@ static void draw_point(EngineState *s, Common::Point p, int start, int width, in
static void draw_polygon(EngineState *s, reg_t polygon, int width, int height) {
SegManager *segMan = s->_segMan;
- reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points));
+ reg_t points = readSelector(segMan, polygon, SELECTOR(points));
#ifdef ENABLE_SCI32
if (segMan->isHeapObject(points))
- points = GET_SEL32(segMan, points, SELECTOR(data));
+ points = readSelector(segMan, points, SELECTOR(data));
#endif
- int size = GET_SEL32V(segMan, polygon, SELECTOR(size));
- int type = GET_SEL32V(segMan, polygon, SELECTOR(type));
+ int size = readSelectorValue(segMan, polygon, SELECTOR(size));
+ int type = readSelectorValue(segMan, polygon, SELECTOR(type));
Common::Point first, prev;
int i;
@@ -386,15 +387,15 @@ static void draw_input(EngineState *s, reg_t poly_list, Common::Point start, Com
}
static void print_polygon(SegManager *segMan, reg_t polygon) {
- reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points));
+ reg_t points = readSelector(segMan, polygon, SELECTOR(points));
#ifdef ENABLE_SCI32
if (segMan->isHeapObject(points))
- points = GET_SEL32(segMan, points, SELECTOR(data));
+ points = readSelector(segMan, points, SELECTOR(data));
#endif
- int size = GET_SEL32V(segMan, polygon, SELECTOR(size));
- int type = GET_SEL32V(segMan, polygon, SELECTOR(type));
+ int size = readSelectorValue(segMan, polygon, SELECTOR(size));
+ int type = readSelectorValue(segMan, polygon, SELECTOR(type));
int i;
Common::Point point;
@@ -436,33 +437,41 @@ static void print_input(EngineState *s, reg_t poly_list, Common::Point start, Co
}
}
+/**
+ * Computes the area of a triangle
+ * Parameters: (const Common::Point &) a, b, c: The points of the triangle
+ * Returns : (int) The area multiplied by two
+ */
static int area(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Computes the area of a triangle
- // Parameters: (const Common::Point &) a, b, c: The points of the triangle
- // Returns : (int) The area multiplied by two
return (b.x - a.x) * (a.y - c.y) - (c.x - a.x) * (a.y - b.y);
}
+/**
+ * Determines whether or not a point is to the left of a directed line
+ * Parameters: (const Common::Point &) a, b: The directed line (a, b)
+ * (const Common::Point &) c: The query point
+ * Returns : (int) true if c is to the left of (a, b), false otherwise
+ */
static bool left(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Determines whether or not a point is to the left of a directed line
- // Parameters: (const Common::Point &) a, b: The directed line (a, b)
- // (const Common::Point &) c: The query point
- // Returns : (int) true if c is to the left of (a, b), false otherwise
return area(a, b, c) > 0;
}
+/**
+ * Determines whether or not three points are collinear
+ * Parameters: (const Common::Point &) a, b, c: The three points
+ * Returns : (int) true if a, b, and c are collinear, false otherwise
+ */
static bool collinear(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Determines whether or not three points are collinear
- // Parameters: (const Common::Point &) a, b, c: The three points
- // Returns : (int) true if a, b, and c are collinear, false otherwise
return area(a, b, c) == 0;
}
+/**
+ * Determines whether or not a point lies on a line segment
+ * Parameters: (const Common::Point &) a, b: The line segment (a, b)
+ * (const Common::Point &) c: The query point
+ * Returns : (int) true if c lies on (a, b), false otherwise
+ */
static bool between(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Determines whether or not a point lies on a line segment
- // Parameters: (const Common::Point &) a, b: The line segment (a, b)
- // (const Common::Point &) c: The query point
- // Returns : (int) true if c lies on (a, b), false otherwise
if (!collinear(a, b, c))
return false;
@@ -473,25 +482,29 @@ static bool between(const Common::Point &a, const Common::Point &b, const Common
return ((a.y <= c.y) && (c.y <= b.y)) || ((a.y >= c.y) && (c.y >= b.y));
}
+/**
+ * Determines whether or not two line segments properly intersect
+ * Parameters: (const Common::Point &) a, b: The line segment (a, b)
+ * (const Common::Point &) c, d: The line segment (c, d)
+ * Returns : (int) true if (a, b) properly intersects (c, d), false otherwise
+ */
static bool intersect_proper(const Common::Point &a, const Common::Point &b, const Common::Point &c, const Common::Point &d) {
- // Determines whether or not two line segments properly intersect
- // Parameters: (const Common::Point &) a, b: The line segment (a, b)
- // (const Common::Point &) c, d: The line segment (c, d)
- // Returns : (int) true if (a, b) properly intersects (c, d), false otherwise
int ab = (left(a, b, c) && left(b, a, d)) || (left(a, b, d) && left(b, a, c));
int cd = (left(c, d, a) && left(d, c, b)) || (left(c, d, b) && left(d, c, a));
return ab && cd;
}
+/**
+ * Polygon containment test
+ * Parameters: (const Common::Point &) p: The point
+ * (Polygon *) polygon: The polygon
+ * Returns : (int) CONT_INSIDE if p is strictly contained in polygon,
+ * CONT_ON_EDGE if p lies on an edge of polygon,
+ * CONT_OUTSIDE otherwise
+ * Number of ray crossing left and right
+ */
static int contained(const Common::Point &p, Polygon *polygon) {
- // Polygon containment test
- // Parameters: (const Common::Point &) p: The point
- // (Polygon *) polygon: The polygon
- // Returns : (int) CONT_INSIDE if p is strictly contained in polygon,
- // CONT_ON_EDGE if p lies on an edge of polygon,
- // CONT_OUTSIDE otherwise
- // Number of ray crossing left and right
int lcross = 0, rcross = 0;
Vertex *vertex;
@@ -549,10 +562,12 @@ static int contained(const Common::Point &p, Polygon *polygon) {
return CONT_OUTSIDE;
}
+/**
+ * Computes polygon area
+ * Parameters: (Polygon *) polygon: The polygon
+ * Returns : (int) The area multiplied by two
+ */
static int polygon_area(Polygon *polygon) {
- // Computes polygon area
- // Parameters: (Polygon *) polygon: The polygon
- // Returns : (int) The area multiplied by two
Vertex *first = polygon->vertices.first();
Vertex *v;
int size = 0;
@@ -567,11 +582,13 @@ static int polygon_area(Polygon *polygon) {
return size;
}
+/**
+ * Fixes the vertex order of a polygon if incorrect. Contained access
+ * polygons should have their vertices ordered clockwise, all other types
+ * anti-clockwise
+ * Parameters: (Polygon *) polygon: The polygon
+ */
static void fix_vertex_order(Polygon *polygon) {
- // Fixes the vertex order of a polygon if incorrect. Contained access
- // polygons should have their vertices ordered clockwise, all other types
- // anti-clockwise
- // Parameters: (Polygon *) polygon: The polygon
int area = polygon_area(polygon);
// When the polygon area is positive the vertices are ordered
@@ -584,13 +601,15 @@ static void fix_vertex_order(Polygon *polygon) {
}
}
+/**
+ * Determines whether or not a line from a point to a vertex intersects the
+ * interior of the polygon, locally at that vertex
+ * Parameters: (Common::Point) p: The point
+ * (Vertex *) vertex: The vertex
+ * Returns : (int) 1 if the line (p, vertex->v) intersects the interior of
+ * the polygon, locally at the vertex. 0 otherwise
+ */
static int inside(const Common::Point &p, Vertex *vertex) {
- // Determines whether or not a line from a point to a vertex intersects the
- // interior of the polygon, locally at that vertex
- // Parameters: (Common::Point) p: The point
- // (Vertex *) vertex: The vertex
- // Returns : (int) 1 if the line (p, vertex->v) intersects the interior of
- // the polygon, locally at the vertex. 0 otherwise
// Check that it's not a single-vertex polygon
if (VERTEX_HAS_EDGES(vertex)) {
const Common::Point &prev = CLIST_PREV(vertex)->v;
@@ -655,28 +674,34 @@ static VertexList *visible_vertices(PathfindingState *s, Vertex *vertex_cur) {
return visVerts;
}
+/**
+ * Determines if a point lies on the screen border
+ * Parameters: (const Common::Point &) p: The point
+ * Returns : (int) true if p lies on the screen border, false otherwise
+ */
bool PathfindingState::pointOnScreenBorder(const Common::Point &p) {
- // Determines if a point lies on the screen border
- // Parameters: (const Common::Point &) p: The point
- // Returns : (int) true if p lies on the screen border, false otherwise
return (p.x == 0) || (p.x == _width - 1) || (p.y == 0) || (p.y == _height - 1);
}
+/**
+ * Determines if an edge lies on the screen border
+ * Parameters: (const Common::Point &) p, q: The edge (p, q)
+ * Returns : (int) true if (p, q) lies on the screen border, false otherwise
+ */
bool PathfindingState::edgeOnScreenBorder(const Common::Point &p, const Common::Point &q) {
- // Determines if an edge lies on the screen border
- // Parameters: (const Common::Point &) p, q: The edge (p, q)
- // Returns : (int) true if (p, q) lies on the screen border, false otherwise
return ((p.x == 0 && q.x == 0) || (p.y == 0 && q.y == 0)
|| ((p.x == _width - 1) && (q.x == _width - 1))
|| ((p.y == _height - 1) && (q.y == _height - 1)));
}
+/**
+ * Searches for a nearby point that is not contained in a polygon
+ * Parameters: (FloatPoint) f: The pointf to search nearby
+ * (Polygon *) polygon: The polygon
+ * Returns : (int) PF_OK on success, PF_FATAL otherwise
+ * (Common::Point) *ret: The non-contained point on success
+ */
static int find_free_point(FloatPoint f, Polygon *polygon, Common::Point *ret) {
- // Searches for a nearby point that is not contained in a polygon
- // Parameters: (FloatPoint) f: The pointf to search nearby
- // (Polygon *) polygon: The polygon
- // Returns : (int) PF_OK on success, PF_FATAL otherwise
- // (Common::Point) *ret: The non-contained point on success
Common::Point p;
// Try nearest point first
@@ -706,12 +731,14 @@ static int find_free_point(FloatPoint f, Polygon *polygon, Common::Point *ret) {
return PF_OK;
}
+/**
+ * Computes the near point of a point contained in a polygon
+ * Parameters: (const Common::Point &) p: The point
+ * (Polygon *) polygon: The polygon
+ * Returns : (int) PF_OK on success, PF_FATAL otherwise
+ * (Common::Point) *ret: The near point of p in polygon on success
+ */
int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Common::Point *ret) {
- // Computes the near point of a point contained in a polygon
- // Parameters: (const Common::Point &) p: The point
- // (Polygon *) polygon: The polygon
- // Returns : (int) PF_OK on success, PF_FATAL otherwise
- // (Common::Point) *ret: The near point of p in polygon on success
Vertex *vertex;
FloatPoint near_p;
uint32 dist = HUGE_DISTANCE;
@@ -751,13 +778,15 @@ int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Co
return find_free_point(near_p, polygon, ret);
}
+/**
+ * Computes the intersection point of a line segment and an edge (not
+ * including the vertices themselves)
+ * Parameters: (const Common::Point &) a, b: The line segment (a, b)
+ * (Vertex *) vertex: The first vertex of the edge
+ * Returns : (int) FP_OK on success, PF_ERROR otherwise
+ * (FloatPoint) *ret: The intersection point
+ */
static int intersection(const Common::Point &a, const Common::Point &b, Vertex *vertex, FloatPoint *ret) {
- // Computes the intersection point of a line segment and an edge (not
- // including the vertices themselves)
- // Parameters: (const Common::Point &) a, b: The line segment (a, b)
- // (Vertex *) vertex: The first vertex of the edge
- // Returns : (int) FP_OK on success, PF_ERROR otherwise
- // (FloatPoint) *ret: The intersection point
// Parameters of parametric equations
float s, t;
// Numerator and denominator of equations
@@ -790,16 +819,18 @@ static int intersection(const Common::Point &a, const Common::Point &b, Vertex *
return PF_ERROR;
}
+/**
+ * Computes the nearest intersection point of a line segment and the polygon
+ * set. Intersection points that are reached from the inside of a polygon
+ * are ignored as are improper intersections which do not obstruct
+ * visibility
+ * Parameters: (PathfindingState *) s: The pathfinding state
+ * (const Common::Point &) p, q: The line segment (p, q)
+ * Returns : (int) PF_OK on success, PF_ERROR when no intersections were
+ * found, PF_FATAL otherwise
+ * (Common::Point) *ret: On success, the closest intersection point
+ */
static int nearest_intersection(PathfindingState *s, const Common::Point &p, const Common::Point &q, Common::Point *ret) {
- // Computes the nearest intersection point of a line segment and the polygon
- // set. Intersection points that are reached from the inside of a polygon
- // are ignored as are improper intersections which do not obstruct
- // visibility
- // Parameters: (PathfindingState *) s: The pathfinding state
- // (const Common::Point &) p, q: The line segment (p, q)
- // Returns : (int) PF_OK on success, PF_ERROR when no intersections were
- // found, PF_FATAL otherwise
- // (Common::Point) *ret: On success, the closest intersection point
Polygon *polygon = 0;
FloatPoint isec;
Polygon *ipolygon = 0;
@@ -903,9 +934,11 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point
case POLY_NEAREST_ACCESS:
if (cont == CONT_INSIDE) {
if (s->_prependPoint != NULL) {
- // We shouldn't get here twice
+ // We shouldn't get here twice.
+ // We need to break in this case, otherwise we'll end in an infinite
+ // loop.
warning("AvoidPath: start point is contained in multiple polygons");
- continue;
+ break;
}
if (s->findNearPoint(start, (*it), new_start) != PF_OK) {
@@ -914,7 +947,7 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point
}
if ((type == POLY_BARRED_ACCESS) || (type == POLY_CONTAINED_ACCESS))
- warning("AvoidPath: start position at unreachable location");
+ debugC(2, kDebugLevelAvoidPath, "AvoidPath: start position at unreachable location");
// The original start position is in an invalid location, so we
// use the moved point and add the original one to the final path
@@ -957,9 +990,14 @@ static Common::Point *fixup_end_point(PathfindingState *s, const Common::Point &
case POLY_NEAREST_ACCESS:
if (cont != CONT_OUTSIDE) {
if (s->_appendPoint != NULL) {
- // We shouldn't get here twice
+ // We shouldn't get here twice.
+ // Happens in LB2CD, inside the speakeasy when walking from the
+ // speakeasy (room 310) into the bathroom (room 320), after having
+ // consulted the notebook (bug #3036299).
+ // We need to break in this case, otherwise we'll end in an infinite
+ // loop.
warning("AvoidPath: end point is contained in multiple polygons");
- continue;
+ break;
}
// The original end position is in an invalid location, so we move the point
@@ -981,14 +1019,16 @@ static Common::Point *fixup_end_point(PathfindingState *s, const Common::Point &
return new_end;
}
+/**
+ * Merges a point into the polygon set. A new vertex is allocated for this
+ * point, unless a matching vertex already exists. If the point is on an
+ * already existing edge that edge is split up into two edges connected by
+ * the new vertex
+ * Parameters: (PathfindingState *) s: The pathfinding state
+ * (const Common::Point &) v: The point to merge
+ * Returns : (Vertex *) The vertex corresponding to v
+ */
static Vertex *merge_point(PathfindingState *s, const Common::Point &v) {
- // Merges a point into the polygon set. A new vertex is allocated for this
- // point, unless a matching vertex already exists. If the point is on an
- // already existing edge that edge is split up into two edges connected by
- // the new vertex
- // Parameters: (PathfindingState *) s: The pathfinding state
- // (const Common::Point &) v: The point to merge
- // Returns : (Vertex *) The vertex corresponding to v
Vertex *vertex;
Vertex *v_new;
Polygon *polygon;
@@ -1029,20 +1069,22 @@ static Vertex *merge_point(PathfindingState *s, const Common::Point &v) {
return v_new;
}
+/**
+ * Converts an SCI polygon into a Polygon
+ * Parameters: (EngineState *) s: The game state
+ * (reg_t) polygon: The SCI polygon to convert
+ * Returns : (Polygon *) The converted polygon, or NULL on error
+ */
static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
- // Converts an SCI polygon into a Polygon
- // Parameters: (EngineState *) s: The game state
- // (reg_t) polygon: The SCI polygon to convert
- // Returns : (Polygon *) The converted polygon, or NULL on error
SegManager *segMan = s->_segMan;
int i;
- reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points));
- int size = GET_SEL32V(segMan, polygon, SELECTOR(size));
+ reg_t points = readSelector(segMan, polygon, SELECTOR(points));
+ int size = readSelectorValue(segMan, polygon, SELECTOR(size));
#ifdef ENABLE_SCI32
// SCI32 stores the actual points in the data property of points (in a new array)
if (segMan->isHeapObject(points))
- points = GET_SEL32(segMan, points, SELECTOR(data));
+ points = readSelector(segMan, points, SELECTOR(data));
#endif
if (size == 0) {
@@ -1050,13 +1092,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
return NULL;
}
- Polygon *poly = new Polygon(GET_SEL32V(segMan, polygon, SELECTOR(type)));
+ Polygon *poly = new Polygon(readSelectorValue(segMan, polygon, SELECTOR(type)));
int skip = 0;
// WORKAROUND: broken polygon in lsl1sci, room 350, after opening elevator
// Polygon has 17 points but size is set to 19
- if ((size == 19) && (s->_gameId == "lsl1sci")) {
+ if ((size == 19) && g_sci->getGameId() == GID_LSL1) {
if ((s->currentRoomNumber() == 350)
&& (read_point(segMan, points, 18) == Common::Point(108, 137))) {
debug(1, "Applying fix for broken polygon in lsl1sci, room 350");
@@ -1074,11 +1116,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
return poly;
}
+/**
+ * Changes the polygon list for optimization level 0 (used for keyboard
+ * support). Totally accessible polygons are removed and near-point
+ * accessible polygons are changed into totally accessible polygons.
+ * Parameters: (PathfindingState *) s: The pathfinding state
+ */
static void change_polygons_opt_0(PathfindingState *s) {
- // Changes the polygon list for optimization level 0 (used for keyboard
- // support). Totally accessible polygons are removed and near-point
- // accessible polygons are changed into totally accessible polygons.
- // Parameters: (PathfindingState *) s: The pathfinding state
PolygonList::iterator it = s->polygons.begin();
while (it != s->polygons.end()) {
@@ -1096,15 +1140,17 @@ static void change_polygons_opt_0(PathfindingState *s) {
}
}
+/**
+ * Converts the SCI input data for pathfinding
+ * Parameters: (EngineState *) s: The game state
+ * (reg_t) poly_list: Polygon list
+ * (Common::Point) start: The start point
+ * (Common::Point) end: The end point
+ * (int) opt: Optimization level (0, 1 or 2)
+ * Returns : (PathfindingState *) On success a newly allocated pathfinding state,
+ * NULL otherwise
+ */
static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Common::Point start, Common::Point end, int width, int height, int opt) {
- // Converts the SCI input data for pathfinding
- // Parameters: (EngineState *) s: The game state
- // (reg_t) poly_list: Polygon list
- // (Common::Point) start: The start point
- // (Common::Point) end: The end point
- // (int) opt: Optimization level (0, 1 or 2)
- // Returns : (PathfindingState *) On success a newly allocated pathfinding state,
- // NULL otherwise
SegManager *segMan = s->_segMan;
Polygon *polygon;
int err;
@@ -1121,7 +1167,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
if (polygon) {
pf_s->polygons.push_back(polygon);
- count += GET_SEL32V(segMan, node->value, SELECTOR(size));
+ count += readSelectorValue(segMan, node->value, SELECTOR(size));
}
node = s->_segMan->lookupNode(node->succ);
@@ -1174,7 +1220,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
// WORKAROUND LSL5 room 660. Priority glitch due to us choosing a different path
// than SSCI. Happens when Patti walks to the control room.
- if ((s->_gameId == "lsl5") && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) {
+ if (g_sci->getGameId() == GID_LSL5 && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) {
debug(1, "[avoidpath] Applying fix for priority problem in LSL5, room 660");
pf_s->_prependPoint = new_start;
new_start = new Common::Point(77, 107);
@@ -1277,7 +1323,7 @@ static void AStar(PathfindingState *s) {
}
if (openSet.empty())
- warning("[avoidpath] End point (%i, %i) is unreachable", s->vertex_end->v.x, s->vertex_end->v.y);
+ debugC(2, kDebugLevelAvoidPath, "AvoidPath: End point (%i, %i) is unreachable", s->vertex_end->v.x, s->vertex_end->v.y);
}
static reg_t allocateOutputArray(SegManager *segMan, int size) {
@@ -1297,11 +1343,13 @@ static reg_t allocateOutputArray(SegManager *segMan, int size) {
return addr;
}
+/**
+ * Stores the final path in newly allocated dynmem
+ * Parameters: (PathfindingState *) p: The pathfinding state
+ * (EngineState *) s: The game state
+ * Returns : (reg_t) Pointer to dynmem containing path
+ */
static reg_t output_path(PathfindingState *p, EngineState *s) {
- // Stores the final path in newly allocated dynmem
- // Parameters: (PathfindingState *) p: The pathfinding state
- // (EngineState *) s: The game state
- // Returns : (reg_t) Pointer to dynmem containing path
int path_len = 0;
reg_t output;
Vertex *vertex = p->vertex_end;
@@ -1394,7 +1442,7 @@ reg_t kAvoidPath(EngineState *s, int argc, reg_t *argv) {
if (argc < 7)
error("[avoidpath] Not enough arguments");
- poly_list = (!argv[4].isNull() ? GET_SEL32(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG);
+ poly_list = (!argv[4].isNull() ? readSelector(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG);
width = argv[5].toUint16();
height = argv[6].toUint16();
if (argc > 7)
@@ -1694,4 +1742,49 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) {
}
}
+/**
+ * This is a quite rare kernel function. An example of when it's called
+ * is in QFG1VGA, after killing any monster.
+ */
+reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) {
+#if 0
+ // 3 parameters: raw polygon data, polygon list, list size
+ reg_t polygonData = argv[0];
+ List *list = s->_segMan->lookupList(argv[1]);
+ Node *node = s->_segMan->lookupNode(list->first);
+ // List size is not needed
+
+ Polygon *polygon;
+ int count = 0;
+
+ while (node) {
+ polygon = convert_polygon(s, node->value);
+
+ if (polygon) {
+ count += readSelectorValue(s->_segMan, node->value, SELECTOR(size));
+ }
+
+ node = s->_segMan->lookupNode(node->succ);
+ }
+#endif
+
+ // TODO: actually merge the polygon. We return an empty polygon for now.
+ // In QFG1VGA, you can walk over enemy bodies after killing them, since
+ // this is a stub.
+ reg_t output = allocateOutputArray(s->_segMan, 1);
+ SegmentRef arrayRef = s->_segMan->dereference(output);
+ writePoint(arrayRef, 0, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
+ warning("Stub: kMergePoly");
+ return output;
+}
+
+#ifdef ENABLE_SCI32
+
+reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) {
+ // kAvoidPath already implements this
+ return kAvoidPath(s, argc, argv);
+}
+
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index ba29f64966..a5501c160f 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -25,16 +25,20 @@
#include "sci/sci.h"
#include "sci/resource.h"
+#include "sci/engine/seg_manager.h"
+#include "sci/engine/script.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
+#include "common/file.h"
+
namespace Sci {
// Loads arbitrary resources of type 'restype' with resource numbers 'resnrs'
// This implementation ignores all resource numbers except the first one.
reg_t kLoad(EngineState *s, int argc, reg_t *argv) {
- int restype = argv[0].toUint16();
+ ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16());
int resnr = argv[1].toUint16();
// Request to dynamically allocate hunk memory for later use
@@ -44,9 +48,32 @@ reg_t kLoad(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, ((restype << 11) | resnr)); // Return the resource identifier as handle
}
+// Unloads an arbitrary resource of type 'restype' with resource numbber 'resnr'
+// behaviour of this call didn't change between sci0->sci1.1 parameter wise, which means getting called with
+// 1 or 3+ parameters is not right according to sierra sci
+reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) {
+ if (argc >= 2) {
+ ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16());
+ reg_t resnr = argv[1];
+
+ // WORKAROUND for a broken script in room 320 in Castle of Dr. Brain.
+ // Script 377 tries to free the hunk memory allocated for the saved area
+ // (underbits) beneath the pop up window, which results in having the
+ // window stay on screen even when it's closed. Ignore this request here.
+ if (restype == kResourceTypeMemory && g_sci->getGameId() == GID_CASTLEBRAIN &&
+ s->currentRoomNumber() == 320)
+ return s->r_acc;
+
+ if (restype == kResourceTypeMemory)
+ s->_segMan->freeHunkEntry(resnr);
+ }
+
+ return s->r_acc;
+}
+
reg_t kLock(EngineState *s, int argc, reg_t *argv) {
int state = argc > 2 ? argv[2].toUint16() : 1;
- ResourceType type = (ResourceType)(argv[0].toUint16() & 0x7f);
+ ResourceType type = g_sci->getResMan()->convertResType(argv[0].toUint16());
ResourceId id = ResourceId(type, argv[1].toUint16());
Resource *which;
@@ -56,42 +83,48 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) {
g_sci->getResMan()->findResource(id, 1);
break;
case 0 :
- which = g_sci->getResMan()->findResource(id, 0);
-
- if (which)
- g_sci->getResMan()->unlockResource(which);
- else {
- if (id.type == kResourceTypeInvalid)
- warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.number, type);
- else
- warning("[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str());
+ if (id.getNumber() == 0xFFFF) {
+ // Unlock all resources of the requested type
+ Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(type);
+ Common::List<ResourceId>::iterator itr = resources->begin();
+
+ while (itr != resources->end()) {
+ Resource *res = g_sci->getResMan()->testResource(*itr);
+ if (res->isLocked())
+ g_sci->getResMan()->unlockResource(res);
+ ++itr;
+ }
+
+ delete resources;
+ } else {
+ which = g_sci->getResMan()->findResource(id, 0);
+
+ if (which)
+ g_sci->getResMan()->unlockResource(which);
+ else {
+ if (id.getType() == kResourceTypeInvalid)
+ warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), type);
+ else
+ // Happens in CD games (e.g. LSL6CD) with the message
+ // resource. It isn't fatal, and it's usually caused
+ // by leftover scripts.
+ debugC(2, kDebugLevelResMan, "[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str());
+ }
}
break;
}
return s->r_acc;
}
-// Unloads an arbitrary resource of type 'restype' with resource numbber 'resnr'
-reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) {
- if (argc >= 2) {
- int restype = argv[0].toUint16();
- reg_t resnr = argv[1];
-
- if (restype == kResourceTypeMemory)
- s->_segMan->freeHunkEntry(resnr);
-
- if (argc > 2)
- warning("kUnload called with more than 2 parameters (%d)", argc);
- } else {
- warning("kUnload called with less than 2 parameters (%d) - ignoring", argc);
- }
-
- return s->r_acc;
-}
-
reg_t kResCheck(EngineState *s, int argc, reg_t *argv) {
Resource *res = NULL;
- ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f);
+ ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16());
+
+ if (restype == kResourceTypeVMD) {
+ char fileName[10];
+ sprintf(fileName, "%d.vmd", argv[1].toUint16());
+ return make_reg(0, Common::File::exists(fileName));
+ }
if ((restype == kResourceTypeAudio36) || (restype == kResourceTypeSync36)) {
if (argc >= 6) {
@@ -111,7 +144,7 @@ reg_t kResCheck(EngineState *s, int argc, reg_t *argv) {
reg_t kClone(EngineState *s, int argc, reg_t *argv) {
reg_t parent_addr = argv[0];
- Object *parent_obj = s->_segMan->getObject(parent_addr);
+ const Object *parent_obj = s->_segMan->getObject(parent_addr);
reg_t clone_addr;
Clone *clone_obj; // same as Object*
@@ -132,7 +165,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) {
*clone_obj = *parent_obj;
// Mark as clone
- clone_obj->setInfoSelector(make_reg(0, SCRIPT_INFO_CLONE));
+ clone_obj->markAsClone();
clone_obj->setSpeciesSelector(clone_obj->getPos());
if (parent_obj->isClass())
clone_obj->setSuperClassSelector(parent_obj->getPos());
@@ -154,25 +187,11 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
- if (victim_obj->getInfoSelector().offset != SCRIPT_INFO_CLONE) {
- //warning("Attempt to dispose something other than a clone at %04x", offset);
+ if (!victim_obj->isClone()) {
// SCI silently ignores this behaviour; some games actually depend on it
return s->r_acc;
}
- // QFG3 clears clones with underbits set
- //if (GET_SEL32V(victim_addr, underBits))
- // warning("Clone %04x:%04x was cleared with underBits set", PRINT_REG(victim_addr));
-
-#if 0
- if (s->dyn_views) { // Free any widget associated with the clone
- GfxWidget *widget = gfxw_set_id(gfxw_remove_ID(s->dyn_views, offset), GFXW_NO_ID);
-
- if (widget && s->bg_widgets)
- s->bg_widgets->add(GFXWC(s->bg_widgets), widget);
- }
-#endif
-
victim_obj->markAsFreed();
return s->r_acc;
@@ -181,7 +200,7 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
// Returns script dispatch address index in the supplied script
reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
int script = argv[0].toUint16();
- int index = (argc > 1) ? argv[1].toUint16() : 0;
+ uint16 index = (argc > 1) ? argv[1].toUint16() : 0;
if (argv[0].segment)
return argv[0];
@@ -193,27 +212,36 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
Script *scr = s->_segMan->getScript(scriptSeg);
- if (!scr->_numExports) {
- // FIXME: Is this fatal? This occurs in SQ4CD
- warning("Script 0x%x does not have a dispatch table", script);
+ if (!scr->getExportsNr()) {
+ // This is normal. Some scripts don't have a dispatch (exports) table,
+ // and this call is probably used to load them in memory, ignoring
+ // the return value. If only one argument is passed, this call is done
+ // only to load the script in memory. Thus, don't show any warning,
+ // as no return value is expected. If an export is requested, then
+ // it will most certainly fail with OOB access.
+ if (argc == 2)
+ error("Script 0x%x does not have a dispatch table and export %d "
+ "was requested from it", script, index);
return NULL_REG;
}
- if (index > scr->_numExports) {
- error("Dispatch index too big: %d > %d", index, scr->_numExports);
+ if (index > scr->getExportsNr()) {
+ error("Dispatch index too big: %d > %d", index, scr->getExportsNr());
return NULL_REG;
}
- return make_reg(scriptSeg, scr->validateExportFunc(index));
+ uint16 address = scr->validateExportFunc(index);
+
+ // Point to the heap for SCI1.1+ games
+ if (getSciVersion() >= SCI_VERSION_1_1)
+ address += scr->getScriptSize();
+
+ return make_reg(scriptSeg, address);
}
reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) {
int script = argv[0].offset;
- // Work around QfG1 graveyard bug
- if (argv[0].segment)
- return s->r_acc;
-
SegmentId id = s->_segMan->getScriptSegment(script);
Script *scr = s->_segMan->getScriptIfLoaded(id);
if (scr) {
@@ -221,13 +249,13 @@ reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) {
scr->setLockers(1);
}
- script_uninstantiate(s->_segMan, script);
+ s->_segMan->uninstantiateScript(script);
if (argc != 2) {
return s->r_acc;
} else {
- // This exists in the KQ5CD and GK1 interpreter. We know it is used when GK1 starts
- // up, before the Sierra logo.
+ // This exists in the KQ5CD and GK1 interpreter. We know it is used
+ // when GK1 starts up, before the Sierra logo.
warning("kDisposeScript called with 2 parameters, still untested");
return argv[1];
}
@@ -244,7 +272,7 @@ reg_t kRespondsTo(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
int selector = argv[1].toUint16();
- return make_reg(0, s->_segMan->isHeapObject(obj) && lookup_selector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone);
+ return make_reg(0, s->_segMan->isHeapObject(obj) && lookupSelector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 367a89005c..2f00cd7da2 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -39,12 +39,39 @@ namespace Sci {
* Used for synthesized music playback
*/
reg_t kDoSound(EngineState *s, int argc, reg_t *argv) {
- return s->_soundCmd->parseCommand(argc, argv, s->r_acc);
+ if (!s)
+ return make_reg(0, g_sci->_features->detectDoSoundType());
+ error("not supposed to call this");
}
+#define CREATE_DOSOUND_FORWARD(_name_) reg_t k##_name_(EngineState *s, int argc, reg_t *argv) { return g_sci->_soundCmd->k##_name_(argc, argv, s->r_acc); }
+
+CREATE_DOSOUND_FORWARD(DoSoundInit)
+CREATE_DOSOUND_FORWARD(DoSoundPlay)
+CREATE_DOSOUND_FORWARD(DoSoundRestore)
+CREATE_DOSOUND_FORWARD(DoSoundDispose)
+CREATE_DOSOUND_FORWARD(DoSoundMute)
+CREATE_DOSOUND_FORWARD(DoSoundStop)
+CREATE_DOSOUND_FORWARD(DoSoundStopAll)
+CREATE_DOSOUND_FORWARD(DoSoundPause)
+CREATE_DOSOUND_FORWARD(DoSoundResumeAfterRestore)
+CREATE_DOSOUND_FORWARD(DoSoundMasterVolume)
+CREATE_DOSOUND_FORWARD(DoSoundUpdate)
+CREATE_DOSOUND_FORWARD(DoSoundFade)
+CREATE_DOSOUND_FORWARD(DoSoundGetPolyphony)
+CREATE_DOSOUND_FORWARD(DoSoundUpdateCues)
+CREATE_DOSOUND_FORWARD(DoSoundSendMidi)
+CREATE_DOSOUND_FORWARD(DoSoundReverb)
+CREATE_DOSOUND_FORWARD(DoSoundSetHold)
+CREATE_DOSOUND_FORWARD(DoSoundDummy)
+CREATE_DOSOUND_FORWARD(DoSoundGetAudioCapability)
+CREATE_DOSOUND_FORWARD(DoSoundSuspend)
+CREATE_DOSOUND_FORWARD(DoSoundSetVolume)
+CREATE_DOSOUND_FORWARD(DoSoundSetPriority)
+CREATE_DOSOUND_FORWARD(DoSoundSetLoop)
+
reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
- case kSciAudioWPlay:
case kSciAudioPlay: {
if (argc < 2)
return NULL_REG;
@@ -72,6 +99,7 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
break;
case kSciAudioPosition:
return make_reg(0, g_sci->_audio->audioCdPosition());
+ case kSciAudioWPlay: // CD Audio can't be preloaded
case kSciAudioRate: // No need to set the audio rate
case kSciAudioVolume: // The speech setting isn't used by CD Audio
case kSciAudioLanguage: // No need to set the language
@@ -80,7 +108,7 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
// Init
return make_reg(0, 1);
default:
- warning("kCdDoAudio: Unhandled case %d", argv[0].toUint16());
+ error("kCdDoAudio: Unhandled case %d", argv[0].toUint16());
}
return s->r_acc;
@@ -110,8 +138,10 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
number = argv[1].toUint16();
} else if (argc == 6 || argc == 8) {
module = argv[1].toUint16();
- number = ((argv[2].toUint16() & 0xff) << 24) | ((argv[3].toUint16() & 0xff) << 16) |
- ((argv[4].toUint16() & 0xff) << 8) | (argv[5].toUint16() & 0xff);
+ number = ((argv[2].toUint16() & 0xff) << 24) |
+ ((argv[3].toUint16() & 0xff) << 16) |
+ ((argv[4].toUint16() & 0xff) << 8) |
+ (argv[5].toUint16() & 0xff);
if (argc == 8)
warning("kDoAudio: Play called with SQ6 extra parameters");
} else {
@@ -119,46 +149,92 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
- return make_reg(0, g_sci->_audio->startAudio(module, number)); // return sample length in ticks
+ debugC(2, kDebugLevelSound, "kDoAudio: play sample %d, module %d", number, module);
+
+ // return sample length in ticks
+ if (argv[0].toUint16() == kSciAudioWPlay)
+ return make_reg(0, g_sci->_audio->wPlayAudio(module, number));
+ else
+ return make_reg(0, g_sci->_audio->startAudio(module, number));
}
case kSciAudioStop:
+ debugC(2, kDebugLevelSound, "kDoAudio: stop");
g_sci->_audio->stopAudio();
break;
case kSciAudioPause:
+ debugC(2, kDebugLevelSound, "kDoAudio: pause");
g_sci->_audio->pauseAudio();
break;
case kSciAudioResume:
+ debugC(2, kDebugLevelSound, "kDoAudio: resume");
g_sci->_audio->resumeAudio();
break;
case kSciAudioPosition:
+ //debugC(2, kDebugLevelSound, "kDoAudio: get position"); // too verbose
return make_reg(0, g_sci->_audio->getAudioPosition());
case kSciAudioRate:
+ debugC(2, kDebugLevelSound, "kDoAudio: set audio rate to %d", argv[1].toUint16());
g_sci->_audio->setAudioRate(argv[1].toUint16());
break;
case kSciAudioVolume: {
int16 volume = argv[1].toUint16();
volume = CLIP<int16>(volume, 0, AUDIO_VOLUME_MAX);
+ debugC(2, kDebugLevelSound, "kDoAudio: set volume to %d", volume);
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2_1) {
+ int16 volumePrev = mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) / 2;
+ volumePrev = CLIP<int16>(volumePrev, 0, AUDIO_VOLUME_MAX);
+ mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2);
+ return make_reg(0, volumePrev);
+ } else
+#endif
mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2);
- break;
}
case kSciAudioLanguage:
// In SCI1.1: tests for digital audio support
- if (getSciVersion() == SCI_VERSION_1_1)
+ if (getSciVersion() == SCI_VERSION_1_1) {
+ debugC(2, kDebugLevelSound, "kDoAudio: audio capability test");
return make_reg(0, 1);
- else {
+ } else {
int16 language = argv[1].toSint16();
+ debugC(2, kDebugLevelSound, "kDoAudio: set language to %d", language);
if (language != -1)
g_sci->getResMan()->setAudioLanguage(language);
- return make_reg(0, g_sci->getSciLanguage());
+ kLanguage kLang = g_sci->getSciLanguage();
+ g_sci->setSciLanguage(kLang);
+
+ return make_reg(0, kLang);
}
break;
case kSciAudioCD:
- return kDoCdAudio(s, argc - 1, argv + 1);
- // TODO: There are 3 more functions used in Freddy Pharkas (11, 12 and 13) and new within sierra sci
- // Details currently unknown
- // kDoAudio sits at seg026:038C
+
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ debugC(2, kDebugLevelSound, "kDoAudio: CD audio subop");
+ return kDoCdAudio(s, argc - 1, argv + 1);
+#ifdef ENABLE_SCI32
+ } else {
+ // TODO: This isn't CD Audio in SCI32 anymore
+ warning("kDoAudio: Unhandled case 10, %d extra arguments passed", argc - 1);
+ break;
+#endif
+ }
+
+ // 3 new subops in Pharkas. kDoAudio in Pharkas sits at seg026:038C
+ case 11:
+ // Not sure where this is used yet
+ warning("kDoAudio: Unhandled case 11, %d extra arguments passed", argc - 1);
+ break;
+ case 12:
+ // Seems to be some sort of audio sync, used in Pharkas. Silenced the
+ // warning due to the high level of spam it produces. (takes no params)
+ //warning("kDoAudio: Unhandled case 12, %d extra arguments passed", argc - 1);
+ break;
+ case 13:
+ // Used in Pharkas whenever a speech sample starts (takes no params)
+ //warning("kDoAudio: Unhandled case 13, %d extra arguments passed", argc - 1);
+ break;
default:
warning("kDoAudio: Unhandled case %d, %d extra arguments passed", argv[0].toUint16(), argc - 1);
}
@@ -195,7 +271,7 @@ reg_t kDoSync(EngineState *s, int argc, reg_t *argv) {
g_sci->_audio->stopSoundSync();
break;
default:
- warning("DoSync: Unhandled subfunction %d", argv[0].toUint16());
+ error("DoSync: Unhandled subfunction %d", argv[0].toUint16());
}
return s->r_acc;
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 426c682e11..9254bce9c1 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -115,12 +115,14 @@ reg_t kStrAt(EngineState *s, int argc, reg_t *argv) {
if (argc > 2) { /* Request to modify this char */
tmp.offset &= 0xff00;
tmp.offset |= newvalue;
+ tmp.segment = 0;
}
} else {
value = tmp.offset >> 8;
if (argc > 2) { /* Request to modify this char */
tmp.offset &= 0x00ff;
tmp.offset |= newvalue << 8;
+ tmp.segment = 0;
}
}
}
@@ -138,10 +140,39 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) {
while (isspace((unsigned char)*source))
source++; /* Skip whitespace */
- if (*source == '$') /* SCI uses this for hex numbers */
- return make_reg(0, (int16)strtol(source + 1, NULL, 16)); /* Hex */
- else
- return make_reg(0, (int16)strtol(source, NULL, 10)); /* Force decimal */
+ int16 result = 0;
+
+ if (*source == '$') {
+ // Hexadecimal input
+ result = (int16)strtol(source + 1, NULL, 16);
+ } else {
+ // Decimal input. We can not use strtol/atoi in here, because while
+ // Sierra used atoi, it was a non standard compliant atoi, that didn't
+ // do clipping. In SQ4 we get the door code in here and that's even
+ // larger than uint32!
+ if (*source == '-') {
+ result = -1;
+ source++;
+ }
+ while (*source) {
+ if ((*source < '0') || (*source > '9')) {
+ // Sierra's atoi stopped processing at anything which is not
+ // a digit. Sometimes the input has a trailing space, that's
+ // fine (example: lsl3)
+ if (*source != ' ') {
+ // TODO: this happens in lsl5 right in the intro -> we get '1' '3' 0xCD 0xCD 0xCD 0xCD 0xCD
+ // find out why this happens and fix it
+ warning("Invalid character in kReadNumber input");
+ }
+ break;
+ }
+ result *= 10;
+ result += *source - 0x30;
+ source++;
+ }
+ }
+
+ return make_reg(0, result);
}
@@ -241,7 +272,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
// If the string is a string object, get to the actual string in the data selector
if (s->_segMan->isObject(reg))
- reg = GET_SEL32(s->_segMan, reg, SELECTOR(data));
+ reg = readSelector(s->_segMan, reg, SELECTOR(data));
#endif
Common::String tempsource = (reg == NULL_REG) ? "" : g_sci->getKernel()->lookupText(reg,
@@ -397,15 +428,16 @@ reg_t kGetFarText(EngineState *s, int argc, reg_t *argv) {
seeker = (char *)textres->data;
- // The second parameter (counter) determines the number of the string inside the text
- // resource.
+ // The second parameter (counter) determines the number of the string
+ // inside the text resource.
while (counter--) {
while (*seeker++)
;
}
- // If the third argument is NULL, allocate memory for the destination. This occurs in
- // SCI1 Mac games. The memory will later be freed by the game's scripts.
+ // If the third argument is NULL, allocate memory for the destination. This
+ // occurs in SCI1 Mac games. The memory will later be freed by the game's
+ // scripts.
if (argv[2] == NULL_REG)
s->_segMan->allocDynmem(strlen(seeker) + 1, "Mac FarText", &argv[2]);
@@ -534,8 +566,8 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv) {
- Common::String quitStr = s->_segMan->getString(argv[0]);
- debug("Setting quit string to '%s'", quitStr.c_str());
+ //Common::String quitStr = s->_segMan->getString(argv[0]);
+ //debug("Setting quit string to '%s'", quitStr.c_str());
return s->r_acc;
}
@@ -552,11 +584,201 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) {
// Make sure target buffer is large enough
SegmentRef buf_r = s->_segMan->dereference(argv[0]);
if (!buf_r.isValid() || buf_r.maxSize < (int)str.size() + 1) {
- warning("StrSplit: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(argv[0]), str.size() + 1, str.c_str());
+ warning("StrSplit: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'",
+ PRINT_REG(argv[0]), str.size() + 1, str.c_str());
return NULL_REG;
}
s->_segMan->strcpy(argv[0], str.c_str());
return argv[0];
}
+#ifdef ENABLE_SCI32
+
+reg_t kText(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0:
+ return kTextSize(s, argc - 1, argv + 1);
+ default:
+ // TODO: Other subops here too, perhaps kTextColors and kTextFonts
+ warning("kText(%d)", argv[0].toUint16());
+ break;
+ }
+
+ return s->r_acc;
+}
+
+reg_t kString(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0: { // New
+ reg_t stringHandle;
+ SciString *string = s->_segMan->allocateString(&stringHandle);
+ string->setSize(argv[1].toUint16());
+
+ // Make sure the first character is a null character
+ if (string->getSize() > 0)
+ string->setValue(0, 0);
+
+ return stringHandle;
+ }
+ case 1: // Size
+ return make_reg(0, s->_segMan->getString(argv[1]).size());
+ case 2: { // At (return value at an index)
+ if (argv[1].segment == s->_segMan->getStringSegmentId())
+ return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]);
+
+ return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]);
+ }
+ case 3: { // Atput (put value at an index)
+ SciString *string = s->_segMan->lookupString(argv[1]);
+
+ uint32 index = argv[2].toUint16();
+ uint32 count = argc - 3;
+
+ if (index + count > 65535)
+ break;
+
+ if (string->getSize() < index + count)
+ string->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ string->setValue(i + index, argv[i + 3].toUint16());
+
+ return argv[1]; // We also have to return the handle
+ }
+ case 4: // Free
+ // Freeing of strings is handled by the garbage collector
+ return s->r_acc;
+ case 5: { // Fill
+ SciString *string = s->_segMan->lookupString(argv[1]);
+ uint16 index = argv[2].toUint16();
+
+ // A count of -1 means fill the rest of the array
+ uint16 count = argv[3].toSint16() == -1 ? string->getSize() - index : argv[3].toUint16();
+ uint16 stringSize = string->getSize();
+
+ if (stringSize < index + count)
+ string->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ string->setValue(i + index, argv[4].toUint16());
+
+ return argv[1];
+ }
+ case 6: { // Cpy
+ const char *string2 = 0;
+ uint32 string2Size = 0;
+
+ if (argv[3].segment == s->_segMan->getStringSegmentId()) {
+ SciString *string = s->_segMan->lookupString(argv[3]);
+ string2 = string->getRawData();
+ string2Size = string->getSize();
+ } else {
+ Common::String string = s->_segMan->getString(argv[3]);
+ string2 = string.c_str();
+ string2Size = string.size() + 1;
+ }
+
+ uint32 index1 = argv[2].toUint16();
+ uint32 index2 = argv[4].toUint16();
+
+ // The original engine ignores bad copies too
+ if (index2 > string2Size)
+ break;
+
+ // A count of -1 means fill the rest of the array
+ uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16();
+
+ // We have a special case here for argv[1] being a system string
+ if (argv[1].segment == s->_segMan->getSysStringsSegment()) {
+ // Resize if necessary
+ const uint16 sysStringId = argv[1].toUint16();
+ SystemString *sysString = s->_segMan->getSystemString(sysStringId);
+ assert(sysString);
+ if ((uint32)sysString->_maxSize < index1 + count) {
+ free(sysString->_value);
+ sysString->_maxSize = index1 + count;
+ sysString->_value = (char *)calloc(index1 + count, sizeof(char));
+ }
+
+ strncpy(sysString->_value + index1, string2 + index2, count);
+ } else {
+ SciString *string1 = s->_segMan->lookupString(argv[1]);
+
+ if (string1->getSize() < index1 + count)
+ string1->setSize(index1 + count);
+
+ // Note: We're accessing from c_str() here because the
+ // string's size ignores the trailing 0 and therefore
+ // triggers an assert when doing string2[i + index2].
+ for (uint16 i = 0; i < count; i++)
+ string1->setValue(i + index1, string2[i + index2]);
+ }
+
+ } return argv[1];
+ case 7: { // Cmp
+ Common::String string1 = argv[1].isNull() ? "" : s->_segMan->getString(argv[1]);
+ Common::String string2 = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
+
+ if (argc == 4) // Strncmp
+ return make_reg(0, strncmp(string1.c_str(), string2.c_str(), argv[3].toUint16()));
+ else // Strcmp
+ return make_reg(0, strcmp(string1.c_str(), string2.c_str()));
+ }
+ case 8: { // Dup
+ const char *rawString = 0;
+ uint32 size = 0;
+
+ if (argv[1].segment == s->_segMan->getStringSegmentId()) {
+ SciString *string = s->_segMan->lookupString(argv[1]);
+ rawString = string->getRawData();
+ size = string->getSize();
+ } else {
+ Common::String string = s->_segMan->getString(argv[1]);
+ rawString = string.c_str();
+ size = string.size() + 1;
+ }
+
+ reg_t stringHandle;
+ SciString *dupString = s->_segMan->allocateString(&stringHandle);
+ dupString->setSize(size);
+
+ for (uint32 i = 0; i < size; i++)
+ dupString->setValue(i, rawString[i]);
+
+ return stringHandle;
+ }
+ case 9: // Getdata
+ if (!s->_segMan->isHeapObject(argv[1]))
+ return argv[1];
+
+ return readSelector(s->_segMan, argv[1], SELECTOR(data));
+ case 10: // Stringlen
+ return make_reg(0, s->_segMan->strlen(argv[1]));
+ case 11: { // Printf
+ reg_t stringHandle;
+ s->_segMan->allocateString(&stringHandle);
+
+ reg_t *adjustedArgs = new reg_t[argc];
+ adjustedArgs[0] = stringHandle;
+ memcpy(&adjustedArgs[1], argv + 1, (argc - 1) * sizeof(reg_t));
+
+ kFormat(s, argc, adjustedArgs);
+ delete[] adjustedArgs;
+ return stringHandle;
+ }
+ case 12: // Printf Buf
+ return kFormat(s, argc - 1, argv + 1);
+ case 13: { // atoi
+ Common::String string = s->_segMan->getString(argv[1]);
+ return make_reg(0, (uint16)atoi(string.c_str()));
+ }
+ default:
+ error("Unknown kString subop %d", argv[0].toUint16());
+ }
+
+ return NULL_REG;
+}
+
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
new file mode 100644
index 0000000000..ac6cfb6835
--- /dev/null
+++ b/engines/sci/engine/kvideo.cpp
@@ -0,0 +1,306 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "engines/util.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/helpers.h"
+#include "sci/graphics/cursor.h"
+#include "sci/graphics/palette.h"
+#include "sci/graphics/screen.h"
+#include "graphics/cursorman.h"
+#include "graphics/video/avi_decoder.h"
+#include "graphics/video/qt_decoder.h"
+#include "sci/video/seq_decoder.h"
+#ifdef ENABLE_SCI32
+#include "graphics/video/coktel_decoder.h"
+#endif
+
+namespace Sci {
+
+void playVideo(Graphics::VideoDecoder *videoDecoder) {
+ if (!videoDecoder)
+ return;
+
+ byte *scaleBuffer = 0;
+ uint16 width = videoDecoder->getWidth();
+ uint16 height = videoDecoder->getHeight();
+ uint16 screenWidth = g_system->getWidth();
+ uint16 screenHeight = g_system->getHeight();
+
+ if (screenWidth == 640 && width <= 320 && height <= 240) {
+ assert(videoDecoder->getPixelFormat().bytesPerPixel == 1);
+ width *= 2;
+ height *= 2;
+ scaleBuffer = new byte[width * height];
+ }
+
+ uint16 x = (screenWidth - width) / 2;
+ uint16 y = (screenHeight - height) / 2;
+ bool skipVideo = false;
+
+ if (videoDecoder->hasDirtyPalette())
+ videoDecoder->setSystemPalette();
+
+ while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
+ if (videoDecoder->needsUpdate()) {
+ Graphics::Surface *frame = videoDecoder->decodeNextFrame();
+ if (frame) {
+ if (scaleBuffer) {
+ // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
+ g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight());
+ g_system->copyRectToScreen(scaleBuffer, width, x, y, width, height);
+ } else
+ g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
+
+ if (videoDecoder->hasDirtyPalette())
+ videoDecoder->setSystemPalette();
+
+ g_system->updateScreen();
+ }
+ }
+
+ Common::Event event;
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+ skipVideo = true;
+ }
+
+ g_system->delayMillis(10);
+ }
+
+ delete[] scaleBuffer;
+ delete videoDecoder;
+}
+
+reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
+ // Hide the cursor if it's showing and then show it again if it was
+ // previously visible.
+ bool reshowCursor = g_sci->_gfxCursor->isVisible();
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelHide();
+
+ uint16 screenWidth = g_system->getWidth();
+ uint16 screenHeight = g_system->getHeight();
+
+ Graphics::VideoDecoder *videoDecoder = 0;
+
+ if (argv[0].segment != 0) {
+ Common::String filename = s->_segMan->getString(argv[0]);
+
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
+ // Mac QuickTime
+ // The only argument is the string for the video
+
+ // HACK: Switch to 16bpp graphics for Cinepak.
+ initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL);
+
+ if (g_system->getScreenFormat().bytesPerPixel == 1) {
+ error("This video requires >8bpp color to be displayed, but could not switch to RGB color mode.");
+ return NULL_REG;
+ }
+
+ videoDecoder = new Graphics::QuickTimeDecoder();
+ if (!videoDecoder->loadFile(filename))
+ error("Could not open '%s'", filename.c_str());
+ } else {
+ // DOS SEQ
+ // SEQ's are called with no subops, just the string and delay
+ SeqDecoder *seqDecoder = new SeqDecoder();
+ seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks
+ videoDecoder = seqDecoder;
+
+ if (!videoDecoder->loadFile(filename)) {
+ warning("Failed to open movie file %s", filename.c_str());
+ delete videoDecoder;
+ videoDecoder = 0;
+ }
+ }
+ } else {
+ // Windows AVI
+ // TODO: This appears to be some sort of subop. case 0 contains the string
+ // for the video, so we'll just play it from there for now.
+
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2_1) {
+ // SCI2.1 always has argv[0] as 1, the rest of the arguments seem to
+ // follow SCI1.1/2.
+ if (argv[0].toUint16() != 1)
+ error("SCI2.1 kShowMovie argv[0] not 1");
+ argv++;
+ argc--;
+ }
+#endif
+ switch (argv[0].toUint16()) {
+ case 0: {
+ Common::String filename = s->_segMan->getString(argv[1]);
+ videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
+
+ if (!videoDecoder->loadFile(filename.c_str())) {
+ warning("Failed to open movie file %s", filename.c_str());
+ delete videoDecoder;
+ videoDecoder = 0;
+ }
+ break;
+ }
+ default:
+ warning("Unhandled SCI kShowMovie subop %d", argv[1].toUint16());
+ }
+ }
+
+ if (videoDecoder) {
+ playVideo(videoDecoder);
+
+ // HACK: Switch back to 8bpp if we played a QuickTime video.
+ // We also won't be copying the screen to the SCI screen...
+ if (g_system->getScreenFormat().bytesPerPixel != 1)
+ initGraphics(screenWidth, screenHeight, screenWidth > 320);
+ else {
+ g_sci->_gfxScreen->kernelSyncWithFramebuffer();
+ g_sci->_gfxPalette->kernelSyncScreenPalette();
+ }
+ }
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelShow();
+
+ return s->r_acc;
+}
+
+#ifdef ENABLE_SCI32
+
+reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
+ uint16 operation = argv[0].toUint16();
+ Graphics::VideoDecoder *videoDecoder = 0;
+ bool reshowCursor = g_sci->_gfxCursor->isVisible();
+ Common::String fileName, warningMsg;
+
+ switch (operation) {
+ case 0: // init
+ // This is actually meant to init the video file, but we play it instead
+ fileName = s->_segMan->derefString(argv[1]);
+ // TODO: argv[2] (usually null). When it exists, it points to an "Event" object,
+ // that holds no data initially (e.g. in the intro of Phantasmagoria 1 demo).
+ // Perhaps it's meant for syncing
+ if (argv[2] != NULL_REG)
+ warning("kPlayVMD: third parameter isn't 0 (it's %04x:%04x - %s)", PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]));
+
+ videoDecoder = new Graphics::VMDDecoder(g_system->getMixer());
+
+ if (!videoDecoder->loadFile(fileName)) {
+ warning("Could not open VMD %s", fileName.c_str());
+ break;
+ }
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelHide();
+
+ playVideo(videoDecoder);
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelShow();
+ break;
+ case 1:
+ {
+ // Set VMD parameters. Called with a maximum of 6 parameters:
+ //
+ // x, y, flags, gammaBoost, gammaFirst, gammaLast
+ //
+ // Flags are as follows:
+ // bit 0 doubled
+ // bit 1 "drop frames"?
+ // bit 2 insert black lines
+ // bit 3 unknown
+ // bit 4 gamma correction
+ // bit 5 hold black frame
+ // bit 6 hold last frame
+ // bit 7 unknown
+ // bit 8 stretch
+
+ // gammaBoost boosts palette colors in the range gammaFirst to
+ // gammaLast, but only if bit 4 in flags is set. Percent value such that
+ // 0% = no amplification These three parameters are optional if bit 4 is
+ // clear. Also note that the x, y parameters play subtle games if used
+ // with subfx 21. The subtleness has to do with creation of temporary
+ // planes and positioning relative to such planes.
+
+ int flags = argv[3].offset;
+ Common::String flagspec;
+
+ if (argc > 3) {
+ if (flags & 1)
+ flagspec += "doubled ";
+ if (flags & 2)
+ flagspec += "dropframes ";
+ if (flags & 4)
+ flagspec += "blacklines ";
+ if (flags & 8)
+ flagspec += "bit3 ";
+ if (flags & 16)
+ flagspec += "gammaboost ";
+ if (flags & 32)
+ flagspec += "holdblack ";
+ if (flags & 64)
+ flagspec += "holdlast ";
+ if (flags & 128)
+ flagspec += "bit7 ";
+ if (flags & 256)
+ flagspec += "stretch";
+
+ warning("VMDFlags: %s", flagspec.c_str());
+ }
+
+ warning("x, y: %d, %d", argv[1].offset, argv[2].offset);
+
+ if (argc > 4 && flags & 16)
+ warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].offset, argv[5].offset, argv[6].offset);
+ break;
+ }
+ case 6:
+ // Play, perhaps? Or stop? This is the last call made, and takes no extra parameters
+ case 14:
+ // Takes an additional integer parameter (e.g. 3)
+ case 16:
+ // Takes an additional parameter, usually 0
+ case 21:
+ // Looks to be setting the video size and position. Called with 4 extra integer
+ // parameters (e.g. 86, 41, 235, 106)
+ default:
+ warningMsg = Common::String::printf("PlayVMD - unsupported subop %d. Params: %d (", operation, argc);
+
+ for (int i = 0; i < argc; i++) {
+ warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += (i == argc - 1 ? ")" : ", ");
+ }
+
+ warning("%s", warningMsg.c_str());
+ break;
+ }
+
+ return s->r_acc;
+}
+
+#endif
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index 07f8792471..6e1b326c4f 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -161,11 +161,13 @@ bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &re
reader = new MessageReaderV4(res->data, res->size);
break;
default:
- warning("Message: unsupported resource version %d", version);
+ error("Message: unsupported resource version %d", version);
return false;
}
if (!reader->init()) {
+ delete reader;
+
warning("Message: failed to read resource header");
return false;
}
@@ -180,6 +182,7 @@ bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &re
continue;
}
+ delete reader;
return false;
}
@@ -193,6 +196,7 @@ bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &re
}
}
+ delete reader;
return true;
}
}
@@ -259,7 +263,7 @@ void MessageState::popCursorStack() {
if (!_cursorStackStack.empty())
_cursorStack = _cursorStackStack.pop();
else
- warning("Message: attempt to pop from empty stack");
+ error("Message: attempt to pop from empty stack");
}
int MessageState::hexDigitToInt(char h) {
@@ -330,7 +334,8 @@ bool MessageState::stringStage(Common::String &outstr, const Common::String &inS
}
// If we find a lowercase character or a digit, it's not a stage direction
- if (((inStr[i] >= 'a') && (inStr[i] <= 'z')) || ((inStr[i] >= '0') && (inStr[i] <= '9')))
+ // SCI32 seems to support having digits in stage directions
+ if (((inStr[i] >= 'a') && (inStr[i] <= 'z')) || ((inStr[i] >= '0') && (inStr[i] <= '9') && (getSciVersion() < SCI_VERSION_2)))
return false;
}
@@ -379,7 +384,14 @@ void MessageState::outputString(reg_t buf, const Common::String &str) {
if ((unsigned)buffer_r.maxSize >= str.size() + 1) {
_segMan->strcpy(buf, str.c_str());
} else {
- warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str());
+ // LSL6 sets an exit text here, but the buffer size allocated
+ // is too small. Don't display a warning in this case, as we
+ // don't use the exit text anyway - bug report #3035533
+ if (g_sci->getGameId() == GID_LSL6 && str.hasPrefix("\r\n(c) 1993 Sierra On-Line, Inc")) {
+ // LSL6 buggy exit text, don't show warning
+ } else {
+ warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str());
+ }
// Set buffer to empty string if possible
if (buffer_r.maxSize > 0)
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index fef2b9a19e..dfc41cc56a 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -33,24 +33,19 @@
#include "sci/event.h"
#include "sci/engine/features.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/message.h"
#include "sci/engine/savegame.h"
+#include "sci/engine/selector.h"
#include "sci/engine/vm_types.h"
#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
-#include "sci/graphics/gui.h"
+#include "sci/graphics/palette.h"
#include "sci/graphics/ports.h"
#include "sci/sound/audio.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/core.h"
-#include "sci/sound/iterator/iterator.h"
-#else
#include "sci/sound/music.h"
-#endif
-#ifdef ENABLE_SCI32
-#include "sci/graphics/gui32.h"
-#endif
+#include "gui/message.h"
namespace Sci {
@@ -58,106 +53,8 @@ namespace Sci {
#define VER(x) Common::Serializer::Version(x)
-// OBSOLETE: This const is used for backward compatibility only.
-const uint32 INTMAPPER_MAGIC_KEY = 0xDEADBEEF;
-
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-// from ksound.cpp:
-SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id);
-#endif
-
-
#pragma mark -
-// TODO: Many of the following sync_*() methods should be turned into member funcs
-// of the classes they are syncing.
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void sync_songlib(Common::Serializer &s, SongLibrary &obj);
-#endif
-
-static void sync_reg_t(Common::Serializer &s, reg_t &obj) {
- s.syncAsUint16LE(obj.segment);
- s.syncAsUint16LE(obj.offset);
-}
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void syncSong(Common::Serializer &s, Song &obj) {
- s.syncAsSint32LE(obj._handle);
- s.syncAsSint32LE(obj._resourceNum);
- s.syncAsSint32LE(obj._priority);
- s.syncAsSint32LE(obj._status);
- s.syncAsSint32LE(obj._restoreBehavior);
- s.syncAsSint32LE(obj._restoreTime);
- s.syncAsSint32LE(obj._loops);
- s.syncAsSint32LE(obj._hold);
-
- if (s.isLoading()) {
- obj._it = 0;
- obj._delay = 0;
- obj._next = 0;
- obj._nextPlaying = 0;
- obj._nextStopping = 0;
- }
-}
-#else
-
-#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff))
-
-void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
- if (s.getVersion() < 14) {
- // Old sound system data. This data is only loaded, never saved (as we're never
- // saving in the older version format)
- uint32 handle = 0;
- s.syncAsSint32LE(handle);
- soundObj = DEFROBNICATE_HANDLE(handle);
- s.syncAsSint32LE(resourceId);
- s.syncAsSint32LE(priority);
- s.syncAsSint32LE(status);
- s.skip(4); // restoreBehavior
- uint32 restoreTime = 0;
- s.syncAsSint32LE(restoreTime);
- ticker = restoreTime * 60 / 1000;
- s.syncAsSint32LE(loop);
- s.syncAsSint32LE(hold);
- // volume and dataInc will be synced from the sound objects
- // when the sound list is reconstructed in gamestate_restore()
- volume = MUSIC_VOLUME_MAX;
- dataInc = 0;
- // No fading info
- fadeTo = 0;
- fadeStep = 0;
- fadeTicker = 0;
- fadeTickerStep = 0;
- } else {
- // A bit more optimized saving
- sync_reg_t(s, soundObj);
- s.syncAsSint16LE(resourceId);
- s.syncAsSint16LE(dataInc);
- s.syncAsSint16LE(ticker);
- s.syncAsSint16LE(signal, VER(17));
- s.syncAsByte(priority);
- s.syncAsSint16LE(loop, VER(17));
- s.syncAsByte(volume);
- s.syncAsByte(hold, VER(17));
- s.syncAsByte(fadeTo);
- s.syncAsSint16LE(fadeStep);
- s.syncAsSint32LE(fadeTicker);
- s.syncAsSint32LE(fadeTickerStep);
- s.syncAsByte(status);
- }
-
- // pMidiParser and pStreamAud will be initialized when the
- // sound list is reconstructed in gamestate_restore()
- if (s.isLoading()) {
- soundRes = 0;
- pMidiParser = 0;
- pStreamAud = 0;
- }
-}
-#endif
-
// Experimental hack: Use syncWithSerializer to sync. By default, this assume
// the object to be synced is a subclass of Serializable and thus tries to invoke
// the saveLoadWithSerializer() method. But it is possible to specialize this
@@ -217,31 +114,19 @@ void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
template <>
void syncWithSerializer(Common::Serializer &s, reg_t &obj) {
- sync_reg_t(s, obj);
+ s.syncAsUint16LE(obj.segment);
+ s.syncAsUint16LE(obj.offset);
}
void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be reserved_id
- s.skip(4, VER(9), VER(18)); // OBSOLETE: Used to be _exportsAreWide
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be gc_mark_bits
+ if (s.isLoading())
+ resetSegMan();
+
+ s.skip(4, VER(14), VER(18)); // OBSOLETE: Used to be _exportsAreWide
if (s.isLoading()) {
// Reset _scriptSegMap, to be restored below
_scriptSegMap.clear();
-
- if (s.getVersion() <= 9) {
- // OBSOLETE: Skip over the old id_seg_map when loading (we now
- // regenerate the equivalent data, in _scriptSegMap, from scratch).
-
- s.skip(4); // base_value
- while (true) {
- uint32 key = 0;
- s.syncAsSint32LE(key);
- if (key == INTMAPPER_MAGIC_KEY)
- break;
- s.skip(4); // idx
- }
- }
}
@@ -257,57 +142,38 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// If we were saving and mobj == 0, or if we are loading and this is an
// entry marked as empty -> skip to next
- if (type == SEG_TYPE_INVALID) {
+ if (type == SEG_TYPE_INVALID)
continue;
- }
-
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be _segManagerId
// Don't save or load HunkTable segments
- if (type == SEG_TYPE_HUNK) {
+ if (type == SEG_TYPE_HUNK)
continue;
- }
- // Handle the OBSOLETE type SEG_TYPE_STRING_FRAG -- just ignore it
- if (s.isLoading() && type == SEG_TYPE_STRING_FRAG) {
- continue;
- }
-
-
- if (s.isLoading()) {
+ if (s.isLoading())
mobj = SegmentObj::createSegmentObj(type);
- }
+
assert(mobj);
// Let the object sync custom data
mobj->saveLoadWithSerializer(s);
// If we are loading a script, hook it up in the script->segment map.
- if (s.isLoading() && type == SEG_TYPE_SCRIPT) {
- _scriptSegMap[((Script *)mobj)->_nr] = i;
- }
+ if (s.isLoading() && type == SEG_TYPE_SCRIPT)
+ _scriptSegMap[((Script *)mobj)->getScriptNumber()] = i;
}
- s.syncAsSint32LE(Clones_seg_id);
- s.syncAsSint32LE(Lists_seg_id);
- s.syncAsSint32LE(Nodes_seg_id);
-}
-
-static void sync_SegManagerPtr(Common::Serializer &s, SegManager *&obj) {
- s.skip(1, VER(9), VER(9)); // obsolete: used to be a flag indicating if we got sci11 or not
+ s.syncAsSint32LE(_clonesSegId);
+ s.syncAsSint32LE(_listsSegId);
+ s.syncAsSint32LE(_nodesSegId);
- if (s.isLoading())
- obj->resetSegMan();
-
- obj->saveLoadWithSerializer(s);
+ syncArray<Class>(s, _classTable);
}
-
template <>
void syncWithSerializer(Common::Serializer &s, Class &obj) {
s.syncAsSint32LE(obj.script);
- sync_reg_t(s, obj.reg);
+ syncWithSerializer(s, obj.reg);
}
static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj) {
@@ -318,60 +184,29 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj)
s.syncVersion(CURRENT_SAVEGAME_VERSION);
obj.savegame_version = s.getVersion();
s.syncString(obj.game_version);
- s.skip(4, VER(9), VER(9)); // obsolete: used to be game version
s.syncAsSint32LE(obj.savegame_date);
s.syncAsSint32LE(obj.savegame_time);
+ if (s.getVersion() < 22) {
+ obj.game_object_offset = 0;
+ obj.script0_size = 0;
+ } else {
+ s.syncAsUint16LE(obj.game_object_offset);
+ s.syncAsUint16LE(obj.script0_size);
+ }
}
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be savegame_version
-
Common::String tmp;
- s.syncString(tmp); // OBSOLETE: Used to be game_version
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be version
-
- // OBSOLETE: Saved menus. Skip all of the saved data
- if (s.getVersion() < 14) {
- int totalMenus = 0;
- s.syncAsUint32LE(totalMenus);
-
- // Now iterate through the obsolete saved menu data
- for (int i = 0; i < totalMenus; i++) {
- s.syncString(tmp); // OBSOLETE: Used to be _title
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _titleWidth
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _width
-
- int menuLength = 0;
- s.syncAsUint32LE(menuLength);
-
- for (int j = 0; j < menuLength; j++) {
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _type
- s.syncString(tmp); // OBSOLETE: Used to be _keytext
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be keytext_size
-
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _flags
- s.skip(64, VER(12), VER(12)); // OBSOLETE: Used to be MENU_SAID_SPEC_SIZE
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _saidPos
- s.syncString(tmp); // OBSOLETE: Used to be _text
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _textPos
- s.skip(4 * 4, VER(12), VER(12)); // OBSOLETE: Used to be _modifiers, _key, _enabled and _tag
- }
- }
- }
-
- s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_foreground
- s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_background
+ s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be game_version
- if (s.getVersion() >= 13 && g_sci->_gui) {
- // Save/Load picPort as well (cause sierra sci also does this)
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ // Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
+ // as the picPort has been changed when loading during the intro
int16 picPortTop, picPortLeft;
Common::Rect picPortRect;
- if (s.isSaving()) {
- // FIXME: _gfxPorts is 0 when using SCI32 code
- assert(g_sci->_gfxPorts);
+ if (s.isSaving())
picPortRect = g_sci->_gfxPorts->kernelGetPicWindow(picPortTop, picPortLeft);
- }
s.syncAsSint16LE(picPortRect.top);
s.syncAsSint16LE(picPortRect.left);
@@ -379,17 +214,15 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint16LE(picPortRect.right);
s.syncAsSint16LE(picPortTop);
s.syncAsSint16LE(picPortLeft);
- }
- sync_SegManagerPtr(s, _segMan);
+ if (s.isLoading())
+ g_sci->_gfxPorts->kernelSetPicWindow(picPortRect, picPortTop, picPortLeft, false);
+ }
- syncArray<Class>(s, _segMan->_classtable);
+ _segMan->saveLoadWithSerializer(s);
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- sync_songlib(s, _sound._songlib);
-#else
- _soundCmd->syncPlayList(s);
-#endif
+ g_sci->_soundCmd->syncPlayList(s);
+ g_sci->_gfxPalette->saveLoadWithSerializer(s);
}
void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) {
@@ -400,8 +233,7 @@ void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) {
void Object::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_flags);
- sync_reg_t(s, _pos);
- s.skip(4, VER(9), VER(12)); // OBSOLETE: Used to be variable_names_nr
+ syncWithSerializer(s, _pos);
s.syncAsSint32LE(_methodCount); // that's actually a uint16
syncArray<reg_t>(s, _variables);
@@ -418,18 +250,18 @@ template <>
void syncWithSerializer(Common::Serializer &s, Table<List>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
- sync_reg_t(s, obj.first);
- sync_reg_t(s, obj.last);
+ syncWithSerializer(s, obj.first);
+ syncWithSerializer(s, obj.last);
}
template <>
void syncWithSerializer(Common::Serializer &s, Table<Node>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
- sync_reg_t(s, obj.pred);
- sync_reg_t(s, obj.succ);
- sync_reg_t(s, obj.key);
- sync_reg_t(s, obj.value);
+ syncWithSerializer(s, obj.pred);
+ syncWithSerializer(s, obj.succ);
+ syncWithSerializer(s, obj.key);
+ syncWithSerializer(s, obj.value);
}
#ifdef ENABLE_SCI32
@@ -463,7 +295,7 @@ void syncWithSerializer(Common::Serializer &s, Table<SciArray<reg_t> >::Entry &o
if (s.isSaving())
value = obj.getValue(i);
- sync_reg_t(s, value);
+ syncWithSerializer(s, value);
if (s.isLoading())
obj.setValue(i, value);
@@ -524,25 +356,15 @@ void HunkTable::saveLoadWithSerializer(Common::Serializer &s) {
void Script::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nr);
- s.syncAsUint32LE(_bufSize);
- s.syncAsUint32LE(_scriptSize);
- s.syncAsUint32LE(_heapSize);
-
- if (s.getVersion() <= 10) {
- assert((s.isLoading()));
- // OBSOLETE: Skip over the old _objIndices data when loading
- s.skip(4); // base_value
- while (true) {
- uint32 key = 0;
- s.syncAsSint32LE(key);
- if (key == INTMAPPER_MAGIC_KEY)
- break;
- s.skip(4); // idx
- }
- }
- s.syncAsSint32LE(_numExports);
- s.syncAsSint32LE(_numSynonyms);
+ if (s.isLoading())
+ init(_nr, g_sci->getResMan());
+ s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _bufSize
+ s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _scriptSize
+ s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _heapSize
+
+ s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numExports
+ s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numSynonyms
s.syncAsSint32LE(_lockers);
// Sync _objects. This is a hashmap, and we use the following on disk format:
@@ -559,18 +381,18 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
_objects.clear();
Object tmp;
for (uint i = 0; i < numObjs; ++i) {
- syncWithSerializer<Object>(s, tmp);
+ syncWithSerializer(s, tmp);
_objects[tmp.getPos().offset] = tmp;
}
} else {
ObjMap::iterator it;
const ObjMap::iterator end = _objects.end();
for (it = _objects.begin(); it != end; ++it) {
- syncWithSerializer<Object>(s, it->_value);
+ syncWithSerializer(s, it->_value);
}
}
- s.syncAsSint32LE(_localsOffset);
+ s.skip(4, VER(14), VER(20)); // OBSOLETE: Used to be _localsOffset
s.syncAsSint32LE(_localsSegment);
s.syncAsSint32LE(_markedAsDeleted);
@@ -615,37 +437,13 @@ void DynMem::saveLoadWithSerializer(Common::Serializer &s) {
void DataStack::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsUint32LE(_capacity);
if (s.isLoading()) {
- //free(entries);
+ free(_entries);
_entries = (reg_t *)calloc(_capacity, sizeof(reg_t));
}
}
#pragma mark -
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void sync_songlib(Common::Serializer &s, SongLibrary &obj) {
- int songcount = 0;
- if (s.isSaving())
- songcount = obj.countSongs();
- s.syncAsUint32LE(songcount);
-
- if (s.isLoading()) {
- obj._lib = 0;
- while (songcount--) {
- Song *newsong = new Song;
- syncSong(s, *newsong);
- obj.addSong(newsong);
- }
- } else {
- Song *seeker = obj._lib;
- while (seeker) {
- seeker->_restoreTime = seeker->_it->getTimepos();
- syncSong(s, *seeker);
- seeker = seeker->_next;
- }
- }
-}
-#else
void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
// Sync song lib data. When loading, the actual song lib will be initialized
// afterwards in gamestate_restore()
@@ -694,7 +492,52 @@ void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
}
}
}
-#endif
+
+void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
+ syncWithSerializer(s, soundObj);
+ s.syncAsSint16LE(resourceId);
+ s.syncAsSint16LE(dataInc);
+ s.syncAsSint16LE(ticker);
+ s.syncAsSint16LE(signal, VER(17));
+ s.syncAsByte(priority);
+ s.syncAsSint16LE(loop, VER(17));
+ s.syncAsByte(volume);
+ s.syncAsByte(hold, VER(17));
+ s.syncAsByte(fadeTo);
+ s.syncAsSint16LE(fadeStep);
+ s.syncAsSint32LE(fadeTicker);
+ s.syncAsSint32LE(fadeTickerStep);
+ s.syncAsByte(status);
+
+ // pMidiParser and pStreamAud will be initialized when the
+ // sound list is reconstructed in gamestate_restore()
+ if (s.isLoading()) {
+ soundRes = 0;
+ pMidiParser = 0;
+ pStreamAud = 0;
+ }
+}
+
+void SoundCommandParser::syncPlayList(Common::Serializer &s) {
+ _music->saveLoadWithSerializer(s);
+}
+
+void SoundCommandParser::reconstructPlayList(int savegame_version) {
+ Common::StackLock lock(_music->_mutex);
+
+ const MusicList::iterator end = _music->getPlayListEnd();
+ for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+ if ((*i)->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resourceId))) {
+ (*i)->soundRes = new SoundResource((*i)->resourceId, _resMan, _soundVersion);
+ _music->soundInitSnd(*i);
+ } else {
+ (*i)->soundRes = 0;
+ }
+ if ((*i)->status == kSoundPlaying) {
+ processPlaySound((*i)->soundObj);
+ }
+ }
+}
#ifdef ENABLE_SCI32
void ArrayTable::saveLoadWithSerializer(Common::Serializer &ser) {
@@ -712,211 +555,153 @@ void StringTable::saveLoadWithSerializer(Common::Serializer &ser) {
}
#endif
-#pragma mark -
-
-
-int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename, const char *version) {
- TimeDate curTime;
- g_system->getTimeAndDate(curTime);
-
- SavegameMetadata meta;
- meta.savegame_version = CURRENT_SAVEGAME_VERSION;
- meta.savegame_name = savename;
- meta.game_version = version;
- meta.savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
- meta.savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
-
- if (s->execution_stack_base) {
- warning("Cannot save from below kernel function");
- return 1;
+void GfxPalette::palVarySaveLoadPalette(Common::Serializer &s, Palette *palette) {
+ s.syncBytes(palette->mapping, 256);
+ s.syncAsUint32LE(palette->timestamp);
+ for (int i = 0; i < 256; i++) {
+ s.syncAsByte(palette->colors[i].used);
+ s.syncAsByte(palette->colors[i].r);
+ s.syncAsByte(palette->colors[i].g);
+ s.syncAsByte(palette->colors[i].b);
}
-
-/*
- if (s->sound_server) {
- if ((s->sound_server->save)(s, dirname)) {
- warning("Saving failed for the sound subsystem");
- //chdir("..");
- return 1;
- }
- }
-*/
- Common::Serializer ser(0, fh);
- sync_SavegameMetadata(ser, meta);
- Graphics::saveThumbnail(*fh);
- s->saveLoadWithSerializer(ser); // FIXME: Error handling?
-
- return 0;
+ s.syncBytes(palette->intensity, 256);
}
-static byte *find_unique_script_block(EngineState *s, byte *buf, int type) {
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
-
- if (oldScriptHeader)
- buf += 2;
-
- do {
- int seeker_type = READ_LE_UINT16(buf);
+void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 24)
+ return;
- if (seeker_type == 0) break;
- if (seeker_type == type) return buf;
+ if (s.isLoading() && _palVaryResourceId != -1)
+ palVaryRemoveTimer();
- int seeker_size = READ_LE_UINT16(buf + 2);
- assert(seeker_size > 0);
- buf += seeker_size;
- } while (1);
+ s.syncAsSint32LE(_palVaryResourceId);
+ if (_palVaryResourceId != -1) {
+ palVarySaveLoadPalette(s, &_palVaryOriginPalette);
+ palVarySaveLoadPalette(s, &_palVaryTargetPalette);
+ s.syncAsSint16LE(_palVaryStep);
+ s.syncAsSint16LE(_palVaryStepStop);
+ s.syncAsSint16LE(_palVaryDirection);
+ s.syncAsUint16LE(_palVaryTicks);
+ s.syncAsSint32LE(_palVaryPaused);
+ }
- return NULL;
+ if (s.isLoading() && _palVaryResourceId != -1) {
+ _palVarySignal = 0;
+ palVaryInstallTimer();
+ }
}
-// TODO: This should probably be turned into an EngineState or DataStack method.
-static void reconstruct_stack(EngineState *retval) {
- SegmentId stack_seg = retval->_segMan->findSegmentByType(SEG_TYPE_STACK);
- DataStack *stack = (DataStack *)(retval->_segMan->_heap[stack_seg]);
-
- retval->stack_base = stack->_entries;
- retval->stack_top = stack->_entries + stack->_capacity;
+void SegManager::reconstructStack(EngineState *s) {
+ DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]);
+ s->stack_base = stack->_entries;
+ s->stack_top = s->stack_base + stack->_capacity;
}
-static void load_script(EngineState *s, Script *scr) {
- scr->_buf = (byte *)malloc(scr->_bufSize);
- assert(scr->_buf);
-
- Resource *script = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, scr->_nr), 0);
- assert(script != 0);
-
- assert(scr->_bufSize >= script->size);
- memcpy(scr->_buf, script->data, script->size);
-
- if (getSciVersion() >= SCI_VERSION_1_1) {
- Resource *heap = g_sci->getResMan()->findResource(ResourceId(kResourceTypeHeap, scr->_nr), 0);
- assert(heap != 0);
-
- scr->_heapStart = scr->_buf + scr->_scriptSize;
-
- assert(scr->_bufSize - scr->_scriptSize <= heap->size);
- memcpy(scr->_heapStart, heap->data, heap->size);
- }
-}
-
-// TODO: Move thie function to a more appropriate place, such as vm.cpp or script.cpp
+// TODO: Move this function to a more appropriate place, such as vm.cpp or script.cpp
void SegManager::reconstructScripts(EngineState *s) {
uint i;
- SegmentObj *mobj;
for (i = 0; i < _heap.size(); i++) {
- mobj = _heap[i];
- if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT)
+ if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
continue;
- Script *scr = (Script *)mobj;
-
- // FIXME: Unify this code with script_instantiate_* ?
- load_script(s, scr);
+ Script *scr = (Script *)_heap[i];
+ scr->load(g_sci->getResMan());
scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
- if (getSciVersion() >= SCI_VERSION_1_1) {
- scr->_exportTable = 0;
- scr->_synonyms = 0;
- if (READ_LE_UINT16(scr->_buf + 6) > 0) {
- scr->setExportTableOffset(6);
- s->_segMan->scriptRelocateExportsSci11(i);
- }
- } else {
- scr->_exportTable = (uint16 *) find_unique_script_block(s, scr->_buf, SCI_OBJ_EXPORTS);
- scr->_synonyms = find_unique_script_block(s, scr->_buf, SCI_OBJ_SYNONYMS);
- scr->_exportTable += 3;
- }
- scr->_codeBlocks.clear();
- ObjMap::iterator it;
- const ObjMap::iterator end = scr->_objects.end();
- for (it = scr->_objects.begin(); it != end; ++it) {
- byte *data = scr->_buf + it->_value.getPos().offset;
- it->_value._baseObj = data;
- }
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
+ it->_value._baseObj = scr->getBuf(it->_value.getPos().offset);
}
for (i = 0; i < _heap.size(); i++) {
- mobj = _heap[i];
- if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT)
+ if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
continue;
- Script *scr = (Script *)mobj;
+ Script *scr = (Script *)_heap[i];
- // FIXME: Unify this code with Script::scriptObjInit ?
- ObjMap::iterator it;
- const ObjMap::iterator end = scr->_objects.end();
- for (it = scr->_objects.begin(); it != end; ++it) {
- byte *data = scr->_buf + it->_value.getPos().offset;
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ reg_t addr = it->_value.getPos();
+ Object *obj = scr->scriptObjInit(addr, false);
- if (getSciVersion() >= SCI_VERSION_1_1) {
- uint16 *funct_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 6 ));
- uint16 *prop_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 4 ));
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ if (!obj->initBaseObject(this, addr, false)) {
+ // TODO/FIXME: This should not be happening at all. It might indicate a possible issue
+ // with the garbage collector. It happens for example in LSL5 (German, perhaps English too).
+ warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
+ scr->scriptObjRemove(addr);
+ }
+ }
+ }
+ }
+}
- it->_value._baseMethod = funct_area;
- it->_value._baseVars = prop_area;
- } else {
- int funct_area = READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET);
- Object *_baseObj;
+void SegManager::reconstructClones() {
+ for (uint i = 0; i < _heap.size(); i++) {
+ SegmentObj *mobj = _heap[i];
+ if (mobj && mobj->getType() == SEG_TYPE_CLONES) {
+ CloneTable *ct = (CloneTable *)mobj;
- _baseObj = s->_segMan->getObject(it->_value.getSpeciesSelector());
+ for (uint j = 0; j < ct->_table.size(); j++) {
+ // Check if the clone entry is used
+ uint entryNum = (uint)ct->first_free;
+ bool isUsed = true;
+ while (entryNum != ((uint) CloneTable::HEAPENTRY_INVALID)) {
+ if (entryNum == j) {
+ isUsed = false;
+ break;
+ }
+ entryNum = ct->_table[entryNum].next_free;
+ }
- if (!_baseObj) {
- warning("Object without a base class: Script %d, index %d (reg address %04x:%04x",
- scr->_nr, i, PRINT_REG(it->_value.getSpeciesSelector()));
+ if (!isUsed)
continue;
- }
- it->_value.setVarCount(_baseObj->getVarCount());
- it->_value._baseObj = _baseObj->_baseObj;
- it->_value._baseMethod = (uint16 *)(data + funct_area);
- it->_value._baseVars = (uint16 *)(data + it->_value.getVarCount() * 2 + SCRIPT_SELECTOR_OFFSET);
- }
- }
- }
+ CloneTable::Entry &seeker = ct->_table[j];
+ const Object *baseObj = getObject(seeker.getSpeciesSelector());
+ seeker.cloneFromObject(baseObj);
+ if (!baseObj) {
+ // Can happen when loading some KQ6 savegames
+ warning("Clone entry without a base class: %d", j);
+ }
+ } // end for
+ } // end if
+ } // end for
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void reconstruct_sounds(EngineState *s) {
- Song *seeker;
- SongIteratorType it_type;
-
- if (getSciVersion() > SCI_VERSION_01)
- it_type = SCI_SONG_ITERATOR_TYPE_SCI1;
- else
- it_type = SCI_SONG_ITERATOR_TYPE_SCI0;
-
- seeker = s->_sound._songlib._lib;
-
- while (seeker) {
- SongIterator *base, *ff = 0;
- int oldstatus;
- SongIterator::Message msg;
-
- base = ff = build_iterator(g_sci->getResMan(), seeker->_resourceNum, it_type, seeker->_handle);
- if (seeker->_restoreBehavior == RESTORE_BEHAVIOR_CONTINUE)
- ff = new_fast_forward_iterator(base, seeker->_restoreTime);
- ff->init();
-
- msg = SongIterator::Message(seeker->_handle, SIMSG_SET_LOOPS(seeker->_loops));
- songit_handle_message(&ff, msg);
- msg = SongIterator::Message(seeker->_handle, SIMSG_SET_HOLD(seeker->_hold));
- songit_handle_message(&ff, msg);
-
- oldstatus = seeker->_status;
- seeker->_status = SOUND_STATUS_STOPPED;
- seeker->_it = ff;
- s->_sound.sfx_song_set_status(seeker->_handle, oldstatus);
- seeker = seeker->_next;
+
+#pragma mark -
+
+
+bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename, const char *version) {
+ TimeDate curTime;
+ g_system->getTimeAndDate(curTime);
+
+ SavegameMetadata meta;
+ meta.savegame_version = CURRENT_SAVEGAME_VERSION;
+ meta.savegame_name = savename;
+ meta.game_version = version;
+ meta.savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ meta.savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+
+ Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
+ meta.script0_size = script0->size;
+ meta.game_object_offset = g_sci->getGameObject().offset;
+
+ // Checking here again
+ if (s->executionStackBase) {
+ warning("Cannot save from below kernel function");
+ return false;
}
+
+ Common::Serializer ser(0, fh);
+ sync_SavegameMetadata(ser, meta);
+ Graphics::saveThumbnail(*fh);
+ s->saveLoadWithSerializer(ser); // FIXME: Error handling?
+
+ return true;
}
-#endif
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
- EngineState *retval;
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongLibrary temp;
-#endif
-
SavegameMetadata meta;
Common::Serializer ser(fh, 0);
@@ -929,104 +714,65 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
if ((meta.savegame_version < MINIMUM_SAVEGAME_VERSION) ||
(meta.savegame_version > CURRENT_SAVEGAME_VERSION)) {
+ /*
if (meta.savegame_version < MINIMUM_SAVEGAME_VERSION)
- warning("Old savegame version detected- can't load");
+ warning("Old savegame version detected, unable to load it");
else
- warning("Savegame version is %d- maximum supported is %0d", meta.savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d, maximum supported is %0d", meta.savegame_version, CURRENT_SAVEGAME_VERSION);
+ */
+
+ GUI::MessageDialog dialog("The format of this saved game is obsolete, unable to load it", "OK");
+ dialog.runModal();
s->r_acc = make_reg(0, 1); // signal failure
return;
}
- if (meta.savegame_version >= 12) {
- // We don't need the thumbnail here, so just read it and discard it
- Graphics::Surface *thumbnail = new Graphics::Surface();
- assert(thumbnail);
- Graphics::loadThumbnail(*fh, *thumbnail);
- delete thumbnail;
- thumbnail = 0;
- }
-
- // Create a new EngineState object
- retval = new EngineState(s->_voc, s->_segMan);
- retval->_event = s->_event;
-
- // Copy some old data
- retval->_soundCmd = s->_soundCmd;
+ if (meta.game_object_offset > 0 && meta.script0_size > 0) {
+ Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
+ if (script0->size != meta.script0_size || g_sci->getGameObject().offset != meta.game_object_offset) {
+ //warning("This saved game was created with a different version of the game, unable to load it");
- // Copy memory segment
- retval->_memorySegmentSize = s->_memorySegmentSize;
- memcpy(retval->_memorySegment, s->_memorySegment, s->_memorySegmentSize);
+ GUI::MessageDialog dialog("This saved game was created with a different version of the game, unable to load it", "OK");
+ dialog.runModal();
- retval->saveLoadWithSerializer(ser); // FIXME: Error handling?
+ s->r_acc = make_reg(0, 1); // signal failure
+ return;
+ }
+ }
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- s->_sound.sfx_exit();
-#endif
+ // We don't need the thumbnail here, so just read it and discard it
+ Graphics::skipThumbnail(*fh);
- // Set exec stack base to zero
- retval->execution_stack_base = 0;
+ s->reset(true);
+ s->saveLoadWithSerializer(ser); // FIXME: Error handling?
// Now copy all current state information
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- temp = retval->_sound._songlib;
- retval->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType());
- retval->sfx_init_flags = s->sfx_init_flags;
- retval->_sound._songlib.freeSounds();
- retval->_sound._songlib = temp;
- retval->_soundCmd->updateSfxState(&retval->_sound);
-#endif
-
- reconstruct_stack(retval);
- retval->_segMan->reconstructScripts(retval);
- retval->_segMan->reconstructClones();
- retval->_gameObj = s->_gameObj;
- retval->script_000 = retval->_segMan->getScript(retval->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD));
- retval->gc_countdown = GC_INTERVAL - 1;
- retval->sys_strings_segment = retval->_segMan->findSegmentByType(SEG_TYPE_SYS_STRINGS);
- retval->sys_strings = (SystemStrings *)(retval->_segMan->_heap[retval->sys_strings_segment]);
+ s->_segMan->reconstructStack(s);
+ s->_segMan->reconstructScripts(s);
+ s->_segMan->reconstructClones();
+ s->initGlobals();
+ s->gcCountDown = GC_INTERVAL - 1;
// Time state:
- retval->last_wait_time = g_system->getMillis();
- retval->game_start_time = g_system->getMillis();
-
- // static parser information:
+ s->lastWaitTime = g_system->getMillis();
+ s->gameStartTime = g_system->getMillis();
+ s->_screenUpdateTime = g_system->getMillis();
- if (retval->_voc)
- retval->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE);
+ if (g_sci->_gfxPorts)
+ g_sci->_gfxPorts->reset();
- retval->successor = NULL;
- retval->_gameId = s->_gameId;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- retval->_sound._it = NULL;
- retval->_sound._flags = s->_sound._flags;
- retval->_sound._song = NULL;
- retval->_sound._suspended = s->_sound._suspended;
- reconstruct_sounds(retval);
-#else
- retval->_soundCmd->reconstructPlayList(meta.savegame_version);
-#endif
+ g_sci->_soundCmd->reconstructPlayList(meta.savegame_version);
// Message state:
- retval->_msgState = new MessageState(retval->_segMan);
-
-#ifdef ENABLE_SCI32
- if (g_sci->_gui32) {
- g_sci->_gui32->init();
- } else {
-#endif
- g_sci->_gui->resetEngineState(retval);
- g_sci->_gui->init(g_sci->_features->usesOldGfxFunctions());
-#ifdef ENABLE_SCI32
- }
-#endif
+ delete s->_msgState;
+ s->_msgState = new MessageState(s->_segMan);
+ s->abortScriptProcessing = kAbortLoadGame;
- s->successor = retval; // Set successor
- script_abort_flag = 2; // Abort current game with replay
- shrink_execution_stack(s, s->execution_stack_base + 1);
+ // signal restored game to game scripts
+ s->gameIsRestarting = GAMEISRESTARTING_RESTORE;
}
bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index bad79fca27..fc254ba33d 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -36,8 +36,8 @@ namespace Sci {
struct EngineState;
enum {
- CURRENT_SAVEGAME_VERSION = 19,
- MINIMUM_SAVEGAME_VERSION = 9
+ CURRENT_SAVEGAME_VERSION = 24,
+ MINIMUM_SAVEGAME_VERSION = 14
};
// Savegame metadata
@@ -47,6 +47,8 @@ struct SavegameMetadata {
Common::String game_version;
int savegame_date;
int savegame_time;
+ uint16 game_object_offset;
+ uint16 script0_size;
};
@@ -57,7 +59,7 @@ struct SavegameMetadata {
* @param savename The description of the savegame
* @return 0 on success, 1 otherwise
*/
-int gamestate_save(EngineState *s, Common::WriteStream *save, const char *savename, const char *version);
+bool gamestate_save(EngineState *s, Common::WriteStream *save, const char *savename, const char *version);
/**
* Restores a game state from a directory.
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 85a07f0efc..3ba550adf9 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -35,567 +35,578 @@
namespace Sci {
-#define END Script_None
-
-opcode_format g_opcode_formats[128][4] = {
- /*00*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*04*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*08*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*0C*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*10*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*14*/
- {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
- /*18*/
- {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
- /*1C*/
- {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
- /*20*/
- {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
- /*24 (24=ret)*/
- {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
- /*28*/
- {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
- /*2C*/
- {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
- /*30*/
- {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
- /*34*/
- {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
- /*38*/
- {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
- /*3C*/
- {Script_None}, {Script_None}, {Script_None}, {Script_Word},
- /*40-4F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*50-5F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*60-6F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*70-7F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
-};
-#undef END
-
-// TODO: script_adjust_opcode_formats should probably be part of the
-// constructor (?) of a VirtualMachine or a ScriptManager class.
-void script_adjust_opcode_formats(EngineState *s) {
- // TODO: Check that this is correct
- if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
- g_opcode_formats[op_lofsa][0] = Script_Offset;
- g_opcode_formats[op_lofss][0] = Script_Offset;
- }
+Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
+ _nr = 0;
+ _buf = NULL;
+ _bufSize = 0;
+ _scriptSize = 0;
+ _heapSize = 0;
+
+ _synonyms = NULL;
+ _heapStart = NULL;
+ _exportTable = NULL;
+
+ _localsOffset = 0;
+ _localsSegment = 0;
+ _localsBlock = NULL;
+ _localsCount = 0;
+
+ _markedAsDeleted = false;
+}
+
+Script::~Script() {
+ freeScript();
+}
+
+void Script::freeScript() {
+ free(_buf);
+ _buf = NULL;
+ _bufSize = 0;
-#ifdef ENABLE_SCI32
- // In SCI32, some arguments are now words instead of bytes
- if (getSciVersion() >= SCI_VERSION_2) {
- g_opcode_formats[op_calle][2] = Script_Word;
- g_opcode_formats[op_callk][1] = Script_Word;
- g_opcode_formats[op_super][1] = Script_Word;
- g_opcode_formats[op_send][0] = Script_Word;
- g_opcode_formats[op_self][0] = Script_Word;
- g_opcode_formats[op_call][1] = Script_Word;
- g_opcode_formats[op_callb][1] = Script_Word;
+ _objects.clear();
+}
+
+void Script::init(int script_nr, ResourceManager *resMan) {
+ Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
+
+ _localsOffset = 0;
+ _localsBlock = NULL;
+ _localsCount = 0;
+
+ _markedAsDeleted = false;
+
+ _nr = script_nr;
+ _buf = 0;
+ _heapStart = 0;
+
+ _scriptSize = script->size;
+ _bufSize = script->size;
+ _heapSize = 0;
+
+ _lockers = 1;
+
+ if (getSciVersion() == SCI_VERSION_0_EARLY) {
+ _bufSize += READ_LE_UINT16(script->data) * 2;
+ } else if (getSciVersion() >= SCI_VERSION_1_1) {
+ // In SCI11, the heap was in a separate space from the script. We append
+ // it to the end of the script, and adjust addressing accordingly.
+ // However, since we address the heap with a 16-bit pointer, the
+ // combined size of the stack and the heap must be 64KB. So far this has
+ // worked for SCI11, SCI2 and SCI21 games. SCI3 games use a different
+ // script format, and theoretically they can exceed the 64KB boundary
+ // using relocation.
+ Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
+ _bufSize += heap->size;
+ _heapSize = heap->size;
+
+ // Ensure that the start of the heap resource can be word-aligned.
+ if (script->size & 2) {
+ _bufSize++;
+ _scriptSize++;
+ }
+
+ // As mentioned above, the script and the heap together should not exceed 64KB
+ if (script->size + heap->size > 65535)
+ error("Script and heap sizes combined exceed 64K. This means a fundamental "
+ "design bug was made regarding SCI1.1 and newer games.\n"
+ "Please report this error to the ScummVM team");
}
-#endif
}
-void SegManager::createClassTable() {
- Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1);
+void Script::load(ResourceManager *resMan) {
+ Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0);
+ assert(script != 0);
- if (!vocab996)
- error("SegManager: failed to open vocab 996");
+ _buf = (byte *)malloc(_bufSize);
+ assert(_buf);
- int totalClasses = vocab996->size >> 2;
- _classtable.resize(totalClasses);
+ assert(_bufSize >= script->size);
+ memcpy(_buf, script->data, script->size);
- for (uint16 classNr = 0; classNr < totalClasses; classNr++) {
- uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2);
+ // Check scripts for matching signatures and patch those, if found
+ matchSignatureAndPatch(_nr, _buf, script->size);
- _classtable[classNr].reg = NULL_REG;
- _classtable[classNr].script = scriptNr;
- }
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0);
+ assert(heap != 0);
- _resMan->unlockResource(vocab996);
-}
+ _heapStart = _buf + _scriptSize;
-reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller) {
- if (classnr == 0xffff)
- return NULL_REG;
+ assert(_bufSize - _scriptSize <= heap->size);
+ memcpy(_heapStart, heap->data, heap->size);
+ }
- if (classnr < 0 || (int)_classtable.size() <= classnr || _classtable[classnr].script < 0) {
- error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classtable.size());
- return NULL_REG;
+ _exportTable = 0;
+ _numExports = 0;
+ _synonyms = 0;
+ _numSynonyms = 0;
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ if (READ_LE_UINT16(_buf + 1 + 5) > 0) { // does the script have an export table?
+ _exportTable = (const uint16 *)(_buf + 1 + 5 + 2);
+ _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1);
+ }
+ _localsOffset = _scriptSize + 4;
+ _localsCount = READ_SCI11ENDIAN_UINT16(_buf + _localsOffset - 2);
} else {
- Class *the_class = &_classtable[classnr];
- if (!the_class->reg.segment) {
- getScriptSegment(the_class->script, lock);
+ _exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS);
+ if (_exportTable) {
+ _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1);
+ _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer)
+ }
+ _synonyms = findBlock(SCI_OBJ_SYNONYMS);
+ if (_synonyms) {
+ _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4;
+ _synonyms += 4; // skip header
+ }
+ const byte* localsBlock = findBlock(SCI_OBJ_LOCALVARS);
+ if (localsBlock) {
+ _localsOffset = localsBlock - _buf + 4;
+ _localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size
+ }
+ }
- if (!the_class->reg.segment) {
- error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
- return NULL_REG;
- }
- } else
- if (caller.segment != the_class->reg.segment)
- getScript(the_class->reg.segment)->incrementLockers();
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ // Does the script actually have locals? If not, set the locals offset to 0
+ if (!_localsCount)
+ _localsOffset = 0;
- return the_class->reg;
+ if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) {
+ error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize);
+ _localsCount = (_bufSize - _localsOffset) >> 1;
+ }
+ } else {
+ // Old script block. There won't be a localvar block in this case.
+ // Instead, the script starts with a 16 bit int specifying the
+ // number of locals we need; these are then allocated and zeroed.
+ _localsCount = READ_LE_UINT16(_buf);
+ _localsOffset = -_localsCount * 2; // Make sure it's invalid
}
}
-void SegManager::scriptInitialiseLocalsZero(SegmentId seg, int count) {
- Script *scr = getScript(seg);
-
- scr->_localsOffset = -count * 2; // Make sure it's invalid
+Object *Script::getObject(uint16 offset) {
+ if (_objects.contains(offset))
+ return &_objects[offset];
+ else
+ return 0;
+}
- LocalVariables *locals = allocLocalsSegment(scr, count);
- if (locals) {
- for (int i = 0; i < count; i++)
- locals->_locals[i] = NULL_REG;
- }
+const Object *Script::getObject(uint16 offset) const {
+ if (_objects.contains(offset))
+ return &_objects[offset];
+ else
+ return 0;
}
-void SegManager::scriptInitialiseLocals(reg_t location) {
- Script *scr = getScript(location.segment);
- unsigned int count;
+Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
+ if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit)
+ obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
- VERIFY(location.offset + 1 < (uint16)scr->_bufSize, "Locals beyond end of script\n");
+ VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n");
- if (getSciVersion() >= SCI_VERSION_1_1)
- count = READ_SCI11ENDIAN_UINT16(scr->_buf + location.offset - 2);
- else
- count = (READ_LE_UINT16(scr->_buf + location.offset - 2) - 4) >> 1;
- // half block size
+ VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n");
- scr->_localsOffset = location.offset;
+ // Get the object at the specified position and init it. This will
+ // automatically "allocate" space for it in the _objects map if necessary.
+ Object *obj = &_objects[obj_pos.offset];
+ obj->init(_buf, obj_pos, fullObjectInit);
- if (!(location.offset + count * 2 + 1 < scr->_bufSize)) {
- warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->_bufSize);
- count = (scr->_bufSize - location.offset) >> 1;
- }
+ return obj;
+}
- LocalVariables *locals = allocLocalsSegment(scr, count);
- if (locals) {
- uint i;
- byte *base = (byte *)(scr->_buf + location.offset);
+void Script::scriptObjRemove(reg_t obj_pos) {
+ if (getSciVersion() < SCI_VERSION_1_1)
+ obj_pos.offset += 8;
- for (i = 0; i < count; i++)
- locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2));
- }
+ _objects.erase(obj_pos.toUint16());
}
-void SegManager::scriptRelocateExportsSci11(SegmentId seg) {
- Script *scr = getScript(seg);
- for (int i = 0; i < scr->_numExports; i++) {
- /* We are forced to use an ugly heuristic here to distinguish function
- exports from object/class exports. The former kind points into the
- script resource, the latter into the heap resource. */
- uint16 location = READ_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i));
+// This helper function is used by Script::relocateLocal and Object::relocate
+// Duplicate in segment.cpp and script.cpp
+static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
+ int rel = location - block_location;
- if ((location < scr->_heapSize - 1) && (READ_SCI11ENDIAN_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) {
- WRITE_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i), location + scr->_heapStart - scr->_buf);
- } else {
- // Otherwise it's probably a function export,
- // and we don't need to do anything.
- }
+ if (rel < 0)
+ return false;
+
+ uint idx = rel >> 1;
+
+ if (idx >= block.size())
+ return false;
+
+ if (rel & 1) {
+ error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
+ return false;
}
+ block[idx].segment = segment; // Perform relocation
+ if (getSciVersion() >= SCI_VERSION_1_1)
+ block[idx].offset += scriptSize;
+
+ return true;
}
-void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) {
- Script *scr = getScript(seg);
- byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2;
+bool Script::relocateLocal(SegmentId segment, int location) {
+ if (_localsBlock)
+ return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
+ else
+ return false;
+}
- while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
- if (READ_SCI11ENDIAN_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) {
- int classpos = seeker - scr->_buf;
- int species = READ_SCI11ENDIAN_UINT16(seeker + 10);
-
- if (species < 0 || species >= (int)_classtable.size()) {
- error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d",
- species, species, _classtable.size(), scr->_nr);
- return;
- }
+void Script::relocate(reg_t block) {
+ const byte *heap = _buf;
+ uint16 heapSize = (uint16)_bufSize;
+ uint16 heapOffset = 0;
- _classtable[species].reg.segment = seg;
- _classtable[species].reg.offset = classpos;
- }
- seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ heap = _heapStart;
+ heapSize = (uint16)_heapSize;
+ heapOffset = _scriptSize;
}
- seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2;
- while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
- reg_t reg;
- Object *obj;
-
- reg.segment = seg;
- reg.offset = seeker - scr->_buf;
- obj = scr->scriptObjInit(reg);
-
-#if 0
- if (obj->_variables[5].offset != 0xffff) {
- obj->_variables[5] = INST_LOOKUP_CLASS(obj->_variables[5].offset);
- baseObj = getObject(obj->_variables[5]);
- obj->variable_names_nr = baseObj->variables_nr;
- obj->_baseObj = baseObj->_baseObj;
+ VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize,
+ "Relocation block outside of script\n");
+
+ int count = READ_SCI11ENDIAN_UINT16(heap + block.offset);
+ int exportIndex = 0;
+ int pos = 0;
+
+ for (int i = 0; i < count; i++) {
+ pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
+ // This occurs in SCI01/SCI1 games where usually one export value is
+ // zero. It seems that in this situation, we should skip the export and
+ // move to the next one, though the total count of valid exports remains
+ // the same
+ if (!pos) {
+ exportIndex++;
+ pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
+ if (!pos)
+ error("Script::relocate(): Consecutive zero exports found");
}
-#endif
- // Copy base from species class, as we need its selector IDs
- obj->setSuperClassSelector(
- getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
-
- // Set the -classScript- selector to the script number.
- // FIXME: As this selector is filled in at run-time, it is likely
- // that it is supposed to hold a pointer. The Obj::isKindOf method
- // uses this selector together with -propDict- to compare classes.
- // For the purpose of Obj::isKindOf, using the script number appears
- // to be sufficient.
- obj->setClassScriptSelector(make_reg(0, scr->_nr));
+ // In SCI0-SCI1, script local variables, objects and code are relocated.
+ // We only relocate locals and objects here, and ignore relocation of
+ // code blocks. In SCI1.1 and newer versions, only locals and objects
+ // are relocated.
+ if (!relocateLocal(block.segment, pos)) {
+ // Not a local? It's probably an object or code block. If it's an object, relocate it.
+ const ObjMap::iterator end = _objects.end();
+ for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
+ if (it->_value.relocate(block.segment, pos, _scriptSize))
+ break;
+ }
- seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
+ exportIndex++;
}
}
+void Script::incrementLockers() {
+ _lockers++;
+}
+void Script::decrementLockers() {
+ if (_lockers > 0)
+ _lockers--;
+}
-int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) {
- *was_new = 1;
+int Script::getLockers() const {
+ return _lockers;
+}
- *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
- if (getSciVersion() >= SCI_VERSION_1_1)
- *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
+void Script::setLockers(int lockers) {
+ _lockers = lockers;
+}
- if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) {
- warning("Script 0x%x requested but not found", script_nr);
- if (getSciVersion() >= SCI_VERSION_1_1) {
- if (*heap)
- warning("Inconsistency: heap resource WAS found");
- else if (*script)
- warning("Inconsistency: script resource WAS found");
- }
+uint16 Script::validateExportFunc(int pubfunct) {
+ bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
+
+ if (_numExports <= pubfunct) {
+ error("validateExportFunc(): pubfunct is invalid");
return 0;
}
- SegmentId seg_id = segMan->getScriptSegment(script_nr);
- Script *scr = segMan->getScriptIfLoaded(seg_id);
- if (scr) {
- if (!scr->isMarkedAsDeleted()) {
- scr->incrementLockers();
- return seg_id;
- } else {
- scr->freeScript();
- }
- } else {
- scr = segMan->allocateScript(script_nr, &seg_id);
- if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US
- error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr);
- return 0;
+ if (exportsAreWide)
+ pubfunct *= 2;
+ uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
+ VERIFY(offset < _bufSize, "invalid export function pointer");
+
+ if (offset == 0) {
+ // Check if the game has a second export table (e.g. script 912 in Camelot)
+ // Fixes bug #3039785
+ const uint16 *secondExportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS, 0);
+
+ if (secondExportTable) {
+ secondExportTable += 3; // skip header plus 2 bytes (secondExportTable is a uint16 pointer)
+ offset = READ_SCI11ENDIAN_UINT16(secondExportTable + pubfunct);
+ VERIFY(offset < _bufSize, "invalid export function pointer");
}
}
- scr->init(script_nr, resMan);
-
- reg_t reg;
- reg.segment = seg_id;
- reg.offset = 0;
+ return offset;
+}
- // Set heap position (beyond the size word)
- scr->setLockers(1);
- scr->setExportTableOffset(0);
- scr->setSynonymsOffset(0);
- scr->setSynonymsNr(0);
+byte *Script::findBlock(int type, int skipBlockIndex) {
+ byte *buf = _buf;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+ int blockIndex = 0;
- *was_new = 0;
+ if (oldScriptHeader)
+ buf += 2;
- return seg_id;
-}
+ do {
+ int seekerType = READ_LE_UINT16(buf);
-#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, addr))
+ if (seekerType == 0)
+ break;
+ if (seekerType == type && blockIndex != skipBlockIndex)
+ return buf;
-int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) {
- int objType;
- uint32 objLength = 0;
- int relocation = -1;
- Resource *script;
- int was_new;
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new);
- uint16 curOffset = oldScriptHeader ? 2 : 0;
+ int seekerSize = READ_LE_UINT16(buf + 2);
+ assert(seekerSize > 0);
+ buf += seekerSize;
+ blockIndex++;
+ } while (1);
- if (was_new)
- return seg_id;
+ return NULL;
+}
- Script *scr = segMan->getScript(seg_id);
- scr->mcpyInOut(0, script->data, script->size);
+// memory operations
- if (oldScriptHeader) {
- // Old script block
- // There won't be a localvar block in this case
- // Instead, the script starts with a 16 bit int specifying the
- // number of locals we need; these are then allocated and zeroed.
- int locals_nr = READ_LE_UINT16(script->data);
- if (locals_nr)
- segMan->scriptInitialiseLocalsZero(seg_id, locals_nr);
+void Script::mcpyInOut(int dst, const void *src, size_t n) {
+ if (_buf) {
+ assert(dst + n <= _bufSize);
+ memcpy(_buf + dst, src, n);
}
+}
- // Now do a first pass through the script objects to find the
- // export table and local variable block
+bool Script::isValidOffset(uint16 offset) const {
+ return offset < _bufSize;
+}
- do {
- objType = scr->getHeap(curOffset);
- if (!objType)
- break;
+SegmentRef Script::dereference(reg_t pointer) {
+ if (pointer.offset > _bufSize) {
+ error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
+ PRINT_REG(pointer), (uint)_bufSize);
+ return SegmentRef();
+ }
- objLength = scr->getHeap(curOffset + 2);
+ SegmentRef ret;
+ ret.isRaw = true;
+ ret.maxSize = _bufSize - pointer.offset;
+ ret.raw = _buf + pointer.offset;
+ return ret;
+}
+
+void Script::initialiseLocals(SegManager *segMan) {
+ LocalVariables *locals = segMan->allocLocalsSegment(this);
+ if (locals) {
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ const byte *base = (const byte *)(_buf + getLocalsOffset());
- // This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the
- // actual cause of it, but the scripts of these demos can't be loaded properly
- // and we're stuck forever in this loop, as objLength never changes
- if (!objLength) {
- warning("script_instantiate_sci0: objLength is 0, unable to parse script");
- return 0;
+ for (uint16 i = 0; i < getLocalsCount(); i++)
+ locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2));
+ } else {
+ // In SCI0 early, locals are set at run time, thus zero them all here
+ for (uint16 i = 0; i < getLocalsCount(); i++)
+ locals->_locals[i] = NULL_REG;
}
+ }
+}
- curOffset += 4; // skip header
+void Script::initialiseClasses(SegManager *segMan) {
+ const byte *seeker = 0;
+ uint16 mult = 0;
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ seeker = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
+ mult = 2;
+ } else {
+ seeker = findBlock(SCI_OBJ_CLASS);
+ mult = 1;
+ }
- switch (objType) {
- case SCI_OBJ_EXPORTS:
- scr->setExportTableOffset(curOffset);
- break;
- case SCI_OBJ_SYNONYMS:
- scr->setSynonymsOffset(curOffset);
- scr->setSynonymsNr((objLength) / 4);
- break;
- case SCI_OBJ_LOCALVARS:
- segMan->scriptInitialiseLocals(make_reg(seg_id, curOffset));
- break;
+ if (!seeker)
+ return;
- case SCI_OBJ_CLASS: {
- int classpos = curOffset - SCRIPT_OBJECT_MAGIC_OFFSET;
- int species = scr->getHeap(curOffset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET);
- if (species < 0 || species >= (int)segMan->_classtable.size()) {
- if (species == (int)segMan->_classtable.size()) {
- // Happens in the LSL2 demo
- warning("Applying workaround for an off-by-one invalid species access");
- segMan->_classtable.resize(segMan->_classtable.size() + 1);
- } else {
- warning("Invalid species %d(0x%x) not in interval "
- "[0,%d) while instantiating script %d\n",
- species, species, segMan->_classtable.size(),
- script_nr);
- return 0;
- }
- }
+ uint16 marker;
+ bool isClass;
+ uint16 classpos;
+ int16 species = 0;
- segMan->_classtable[species].reg.segment = seg_id;
- segMan->_classtable[species].reg.offset = classpos;
- // Set technical class position-- into the block allocated for it
- }
- break;
+ while (true) {
+ // In SCI0-SCI1, this is the segment type. In SCI11, it's a marker (0x1234)
+ marker = READ_SCI11ENDIAN_UINT16(seeker);
+ classpos = seeker - _buf;
- default:
+ if (!marker)
break;
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ isClass = (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass); // -info- selector
+ species = READ_SCI11ENDIAN_UINT16(seeker + 10);
+ } else {
+ isClass = (marker == SCI_OBJ_CLASS);
+ if (isClass)
+ species = READ_SCI11ENDIAN_UINT16(seeker + 12);
+ classpos += 12;
+ }
+
+ if (isClass) {
+ // WORKAROUNDs for off-by-one script errors
+ if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo() && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
+ if (g_sci->getGameId() == GID_LSL3 && !g_sci->isDemo() && _nr == 500 && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
+ if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 93 && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
+ if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 99 && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
+
+ if (species < 0 || species >= (int)segMan->classTableSize())
+ error("Invalid species %d(0x%x) unknown max %d(0x%x) while instantiating script %d\n",
+ species, species, segMan->classTableSize(), segMan->classTableSize(), _nr);
+
+ SegmentId segmentId = segMan->getScriptSegment(_nr);
+ segMan->setClassOffset(species, make_reg(segmentId, classpos));
}
- curOffset += objLength - 4;
- } while (objType != 0 && curOffset < script->size - 2);
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * mult;
+ }
+}
- // And now a second pass to adjust objects and class pointers, and the general pointers
- objLength = 0;
- curOffset = oldScriptHeader ? 2 : 0;
+void Script::initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId) {
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+ const byte *seeker = _buf + (oldScriptHeader ? 2 : 0);
do {
- objType = scr->getHeap(curOffset);
+ uint16 objType = READ_SCI11ENDIAN_UINT16(seeker);
if (!objType)
break;
- objLength = scr->getHeap(curOffset + 2);
- curOffset += 4; // skip header
-
- reg_t addr = make_reg(seg_id, curOffset);
-
switch (objType) {
- case SCI_OBJ_CODE:
- scr->scriptAddCodeBlock(addr);
- break;
case SCI_OBJ_OBJECT:
- case SCI_OBJ_CLASS: { // object or class?
- Object *obj = scr->scriptObjInit(addr);
-
- // Instantiate the superclass, if neccessary
- obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset));
-
- Object *baseObj = segMan->getObject(obj->getSpeciesSelector());
-
- if (baseObj) {
- obj->setVarCount(baseObj->getVarCount());
- // Copy base from species class, as we need its selector IDs
- obj->_baseObj = baseObj->_baseObj;
-
- obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset));
- } else {
- warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
-
- scr->scriptObjRemove(addr);
+ case SCI_OBJ_CLASS:
+ {
+ reg_t addr = make_reg(segmentId, seeker - _buf + 4);
+ Object *obj = scriptObjInit(addr);
+ obj->initSpecies(segMan, addr);
+
+ if (!obj->initBaseObject(segMan, addr)) {
+ if (_nr == 202 && g_sci->getGameId() == GID_KQ5) {
+ // WORKAROUND: Script 202 of KQ5 French and German
+ // (perhaps Spanish too?) has an invalid object.
+ // This is non-fatal. Refer to bug #3035396.
+ } else {
+ error("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
+ }
+ scriptObjRemove(addr);
+ }
}
- } // if object or class
- break;
- case SCI_OBJ_POINTERS: // A relocation table
- relocation = addr.offset;
break;
default:
break;
}
- curOffset += objLength - 4;
- } while (objType != 0 && curOffset < script->size - 2);
-
- if (relocation >= 0)
- scr->scriptRelocate(make_reg(seg_id, relocation));
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
+ } while ((uint32)(seeker - _buf) < getScriptSize() - 2);
- return seg_id; // instantiation successful
+ byte *relocationBlock = findBlock(SCI_OBJ_POINTERS);
+ if (relocationBlock)
+ relocate(make_reg(segmentId, relocationBlock - getBuf() + 4));
}
-int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) {
- Resource *script, *heap;
- int _heapStart;
- reg_t reg;
- int was_new;
-
- const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new);
+void Script::initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId) {
+ const byte *seeker = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
- if (was_new)
- return seg_id;
-
- Script *scr = segMan->getScript(seg_id);
-
- _heapStart = script->size;
- if (script->size & 2)
- _heapStart++;
-
- scr->mcpyInOut(0, script->data, script->size);
- scr->mcpyInOut(_heapStart, heap->data, heap->size);
-
- if (READ_SCI11ENDIAN_UINT16(script->data + 6) > 0)
- scr->setExportTableOffset(6);
+ while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
+ reg_t reg = make_reg(segmentId, seeker - _buf);
+ Object *obj = scriptObjInit(reg);
- reg.segment = seg_id;
- reg.offset = _heapStart + 4;
- segMan->scriptInitialiseLocals(reg);
+ // Copy base from species class, as we need its selector IDs
+ obj->setSuperClassSelector(
+ segMan->getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
+
+ // If object is instance, get -propDict- from class and set it for this
+ // object. This is needed for ::isMemberOf() to work.
+ // Example testcase - room 381 of sq4cd - if isMemberOf() doesn't work,
+ // talk-clicks on the robot will act like clicking on ego
+ if (!obj->isClass()) {
+ reg_t classObject = obj->getSuperClassSelector();
+ const Object *classObj = segMan->getObject(classObject);
+ obj->setPropDictSelector(classObj->getPropDictSelector());
+ }
- segMan->scriptRelocateExportsSci11(seg_id);
- segMan->scriptInitialiseObjectsSci11(seg_id);
+ // Set the -classScript- selector to the script number.
+ // FIXME: As this selector is filled in at run-time, it is likely
+ // that it is supposed to hold a pointer. The Obj::isKindOf method
+ // uses this selector together with -propDict- to compare classes.
+ // For the purpose of Obj::isKindOf, using the script number appears
+ // to be sufficient.
+ obj->setClassScriptSelector(make_reg(0, _nr));
- reg.offset = READ_SCI11ENDIAN_UINT16(heap->data);
- scr->heapRelocate(reg);
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
+ }
- return seg_id;
+ relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(_heapStart)));
}
-int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) {
- if (getSciVersion() >= SCI_VERSION_1_1)
- return script_instantiate_sci11(resMan, segMan, script_nr);
- else
- return script_instantiate_sci0(resMan, segMan, script_nr);
+reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const {
+ addr.offset = 0;
+ return addr;
}
-void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) {
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- reg_t reg = make_reg(seg, oldScriptHeader ? 2 : 0);
- int objType, objLength;
- Script *scr = segMan->getScript(seg);
-
- // Make a pass over the object in order uninstantiate all superclasses
- objLength = 0;
-
- do {
- reg.offset += objLength; // Step over the last checked object
-
- objType = scr->getHeap(reg.offset);
- if (!objType)
- break;
- objLength = scr->getHeap(reg.offset + 2); // use SEG_UGET_HEAP ??
-
- reg.offset += 4; // Step over header
-
- if ((objType == SCI_OBJ_OBJECT) || (objType == SCI_OBJ_CLASS)) { // object or class?
- int superclass;
+void Script::freeAtAddress(SegManager *segMan, reg_t addr) {
+ /*
+ debugC(2, kDebugLevelGC, "[GC] Freeing script %04x:%04x", PRINT_REG(addr));
+ if (_localsSegment)
+ debugC(2, kDebugLevelGC, "[GC] Freeing locals %04x:0000", _localsSegment);
+ */
- reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET;
-
- superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass...
-
- if (superclass >= 0) {
- int superclass_script = segMan->_classtable[superclass].script;
-
- if (superclass_script == script_nr) {
- if (scr->getLockers())
- scr->decrementLockers(); // Decrease lockers if this is us ourselves
- } else
- script_uninstantiate(segMan, superclass_script);
- // Recurse to assure that the superclass lockers number gets decreased
- }
-
- reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
- } // if object or class
-
- reg.offset -= 4; // Step back on header
-
- } while (objType != 0);
+ if (_markedAsDeleted)
+ segMan->deallocateScript(_nr);
}
-void script_uninstantiate(SegManager *segMan, int script_nr) {
- SegmentId segment = segMan->getScriptSegment(script_nr);
- Script *scr = segMan->getScriptIfLoaded(segment);
+Common::Array<reg_t> Script::listAllDeallocatable(SegmentId segId) const {
+ const reg_t r = make_reg(segId, 0);
+ return Common::Array<reg_t>(&r, 1);
+}
- if (!scr) { // Is it already loaded?
- //warning("unloading script 0x%x requested although not loaded", script_nr);
- // This is perfectly valid SCI behaviour
- return;
+Common::Array<reg_t> Script::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
+ if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) {
+ const Object *obj = getObject(addr.offset);
+ if (obj) {
+ // Note all local variables, if we have a local variable environment
+ if (_localsSegment)
+ tmp.push_back(make_reg(_localsSegment, 0));
+
+ for (uint i = 0; i < obj->getVarCount(); i++)
+ tmp.push_back(obj->getVariable(i));
+ } else {
+ error("Request for outgoing script-object reference at %04x:%04x failed", PRINT_REG(addr));
+ }
+ } else {
+ /* warning("Unexpected request for outgoing script-object references at %04x:%04x", PRINT_REG(addr));*/
+ /* Happens e.g. when we're looking into strings */
}
+ return tmp;
+}
- scr->decrementLockers(); // One less locker
-
- if (scr->getLockers() > 0)
- return;
-
- // Free all classtable references to this script
- for (uint i = 0; i < segMan->_classtable.size(); i++)
- if (segMan->_classtable[i].reg.segment == segment)
- segMan->_classtable[i].reg = NULL_REG;
-
- if (getSciVersion() < SCI_VERSION_1_1)
- script_uninstantiate_sci0(segMan, script_nr, segment);
- // FIXME: Add proper script uninstantiation for SCI 1.1
-
- if (scr->getLockers())
- return; // if xxx.lockers > 0
+Common::Array<reg_t> Script::listObjectReferences() const {
+ Common::Array<reg_t> tmp;
- // Otherwise unload it completely
- // Explanation: I'm starting to believe that this work is done by SCI itself.
- scr->markDeleted();
+ // Locals, if present
+ if (_localsSegment)
+ tmp.push_back(make_reg(_localsSegment, 0));
- debugC(kDebugLevelScripts, "Unloaded script 0x%x.", script_nr);
+ // All objects (may be classes, may be indirectly reachable)
+ ObjMap::iterator it;
+ const ObjMap::iterator end = _objects.end();
+ for (it = _objects.begin(); it != end; ++it) {
+ tmp.push_back(it->_value.getPos());
+ }
- return;
+ return tmp;
}
-
} // End of namespace Sci
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index e94e9f64e6..c60cc4b19f 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -27,13 +27,13 @@
#define SCI_ENGINE_SCRIPT_H
#include "common/str.h"
+#include "sci/engine/segment.h"
namespace Sci {
struct EngineState;
class ResourceManager;
-
-#define SCI_SCRIPTS_NR 1000
+struct SciScriptSignature;
enum ScriptObjectTypes {
SCI_OBJ_TERMINATOR,
@@ -49,160 +49,218 @@ enum ScriptObjectTypes {
SCI_OBJ_LOCALVARS
};
-// Opcode formats
-enum opcode_format {
- Script_Invalid = -1,
- Script_None = 0,
- Script_Byte,
- Script_SByte,
- Script_Word,
- Script_SWord,
- Script_Variable,
- Script_SVariable,
- Script_SRelative,
- Script_Property,
- Script_Global,
- Script_Local,
- Script_Temp,
- Script_Param,
- Script_Offset,
- Script_End
-};
+typedef Common::HashMap<uint16, Object> ObjMap;
-enum sci_opcodes {
- op_bnot = 0x00, // 000
- op_add = 0x01, // 001
- op_sub = 0x02, // 002
- op_mul = 0x03, // 003
- op_div = 0x04, // 004
- op_mod = 0x05, // 005
- op_shr = 0x06, // 006
- op_shl = 0x07, // 007
- op_xor = 0x08, // 008
- op_and = 0x09, // 009
- op_or = 0x0a, // 010
- op_neg = 0x0b, // 011
- op_not = 0x0c, // 012
- op_eq_ = 0x0d, // 013
- op_ne_ = 0x0e, // 014
- op_gt_ = 0x0f, // 015
- op_ge_ = 0x10, // 016
- op_lt_ = 0x11, // 017
- op_le_ = 0x12, // 018
- op_ugt_ = 0x13, // 019
- op_uge_ = 0x14, // 020
- op_ult_ = 0x15, // 021
- op_ule_ = 0x16, // 022
- op_bt = 0x17, // 023
- op_bnt = 0x18, // 024
- op_jmp = 0x19, // 025
- op_ldi = 0x1a, // 026
- op_push = 0x1b, // 027
- op_pushi = 0x1c, // 028
- op_toss = 0x1d, // 029
- op_dup = 0x1e, // 030
- op_link = 0x1f, // 031
- op_call = 0x20, // 032
- op_callk = 0x21, // 033
- op_callb = 0x22, // 034
- op_calle = 0x23, // 035
- op_ret = 0x24, // 036
- op_send = 0x25, // 037
- // dummy 0x26, // 038
- // dummy 0x27, // 039
- op_class = 0x28, // 040
- // dummy 0x29, // 041
- op_self = 0x2a, // 042
- op_super = 0x2b, // 043
- op_rest = 0x2c, // 044
- op_lea = 0x2d, // 045
- op_selfID = 0x2e, // 046
- // dummy 0x2f // 047
- op_pprev = 0x30, // 048
- op_pToa = 0x31, // 049
- op_aTop = 0x32, // 050
- op_pTos = 0x33, // 051
- op_sTop = 0x34, // 052
- op_ipToa = 0x35, // 053
- op_dpToa = 0x36, // 054
- op_ipTos = 0x37, // 055
- op_dpTos = 0x38, // 056
- op_lofsa = 0x39, // 057
- op_lofss = 0x3a, // 058
- op_push0 = 0x3b, // 059
- op_push1 = 0x3c, // 060
- op_push2 = 0x3d, // 061
- op_pushSelf = 0x3e, // 062
- op_line = 0x3f, // 063
- op_lag = 0x40, // 064
- op_lal = 0x41, // 065
- op_lat = 0x42, // 066
- op_lap = 0x43, // 067
- op_lsg = 0x44, // 068
- op_lsl = 0x45, // 069
- op_lst = 0x46, // 070
- op_lsp = 0x47, // 071
- op_lagi = 0x48, // 072
- op_lali = 0x49, // 073
- op_lati = 0x4a, // 074
- op_lapi = 0x4b, // 075
- op_lsgi = 0x4c, // 076
- op_lsli = 0x4d, // 077
- op_lsti = 0x4e, // 078
- op_lspi = 0x4f, // 079
- op_sag = 0x50, // 080
- op_sal = 0x51, // 081
- op_sat = 0x52, // 082
- op_sap = 0x53, // 083
- op_ssg = 0x54, // 084
- op_ssl = 0x55, // 085
- op_sst = 0x56, // 086
- op_ssp = 0x57, // 087
- op_sagi = 0x58, // 088
- op_sali = 0x59, // 089
- op_sati = 0x5a, // 090
- op_sapi = 0x5b, // 091
- op_ssgi = 0x5c, // 092
- op_ssli = 0x5d, // 093
- op_ssti = 0x5e, // 094
- op_sspi = 0x5f, // 095
- op_plusag = 0x60, // 096
- op_plusal = 0x61, // 097
- op_plusat = 0x62, // 098
- op_plusap = 0x63, // 099
- op_plussg = 0x64, // 100
- op_plussl = 0x65, // 101
- op_plusst = 0x66, // 102
- op_plussp = 0x67, // 103
- op_plusagi = 0x68, // 104
- op_plusali = 0x69, // 105
- op_plusati = 0x6a, // 106
- op_plusapi = 0x6b, // 107
- op_plussgi = 0x6c, // 108
- op_plussli = 0x6d, // 109
- op_plussti = 0x6e, // 110
- op_plusspi = 0x6f, // 111
- op_minusag = 0x70, // 112
- op_minusal = 0x71, // 113
- op_minusat = 0x72, // 114
- op_minusap = 0x73, // 115
- op_minussg = 0x74, // 116
- op_minussl = 0x75, // 117
- op_minusst = 0x76, // 118
- op_minussp = 0x77, // 119
- op_minusagi = 0x78, // 120
- op_minusali = 0x79, // 121
- op_minusati = 0x7a, // 122
- op_minusapi = 0x7b, // 123
- op_minussgi = 0x7c, // 124
- op_minussli = 0x7d, // 125
- op_minussti = 0x7e, // 126
- op_minusspi = 0x7f // 127
-};
+class Script : public SegmentObj {
+private:
+ int _nr; /**< Script number */
+ byte *_buf; /**< Static data buffer, or NULL if not used */
+ byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
+
+ int _lockers; /**< Number of classes and objects that require this script */
+ size_t _scriptSize;
+ size_t _heapSize;
+ uint16 _bufSize;
+
+ const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
+ uint16 _numExports; /**< Number of entries in the exports table */
+
+ const byte *_synonyms; /**< Synonyms block or 0 if not present*/
+ uint16 _numSynonyms; /**< Number of entries in the synonyms block */
+
+ int _localsOffset;
+ uint16 _localsCount;
+
+ bool _markedAsDeleted;
+
+public:
+ /**
+ * Table for objects, contains property variables.
+ * Indexed by the TODO offset.
+ */
+ ObjMap _objects;
+ SegmentId _localsSegment; /**< The local variable segment */
+ LocalVariables *_localsBlock;
+
+public:
+ int getLocalsOffset() const { return _localsOffset; }
+ uint16 getLocalsCount() const { return _localsCount; }
+
+ uint32 getScriptSize() const { return _scriptSize; }
+ uint32 getHeapSize() const { return _heapSize; }
+ uint32 getBufSize() const { return _bufSize; }
+ const byte *getBuf(uint offset = 0) const { return _buf + offset; }
+
+ int getScriptNumber() const { return _nr; }
+
+public:
+ Script();
+ ~Script();
+
+ void freeScript();
+ void init(int script_nr, ResourceManager *resMan);
+ void load(ResourceManager *resMan);
+
+ void matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize);
+ int32 findSignature(const SciScriptSignature *signature, const byte *scriptData, const uint32 scriptSize);
+ void applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset);
+
+ virtual bool isValidOffset(uint16 offset) const;
+ virtual SegmentRef dereference(reg_t pointer);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
+ virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
+
+ /**
+ * Return a list of all references to objects in this script
+ * (and also to the locals segment, if any).
+ * Used by the garbage collector.
+ * @return a list of outgoing references within the object
+ */
+ Common::Array<reg_t> listObjectReferences() const;
+
+ virtual void saveLoadWithSerializer(Common::Serializer &ser);
+
+ Object *getObject(uint16 offset);
+ const Object *getObject(uint16 offset) const;
+
+ /**
+ * Initializes an object within the segment manager
+ * @param obj_pos Location (segment, offset) of the object. It must
+ * point to the beginning of the script/class block
+ * (as opposed to what the VM considers to be the
+ * object location)
+ * @returns A newly created Object describing the object,
+ * stored within the relevant script
+ */
+ Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true);
-extern opcode_format g_opcode_formats[128][4];
+ /**
+ * Removes a script object
+ * @param obj_pos Location (segment, offset) of the object.
+ */
+ void scriptObjRemove(reg_t obj_pos);
-void script_adjust_opcode_formats(EngineState *s);
+ /**
+ * Initializes the script's local variables
+ * @param segMan A reference to the segment manager
+ */
+ void initialiseLocals(SegManager *segMan);
+
+ /**
+ * Adds the script's classes to the segment manager's class table
+ * @param segMan A reference to the segment manager
+ */
+ void initialiseClasses(SegManager *segMan);
+
+ /**
+ * Initializes the script's objects (SCI0)
+ * @param segMan A reference to the segment manager
+ * @param segmentId The script's segment id
+ */
+ void initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId);
+
+ /**
+ * Initializes the script's objects (SCI1.1+)
+ * @param segMan A reference to the segment manager
+ * @param segmentId The script's segment id
+ */
+ void initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId);
+
+ // script lock operations
+
+ /** Increments the number of lockers of this script by one. */
+ void incrementLockers();
+
+ /** Decrements the number of lockers of this script by one. */
+ void decrementLockers();
+
+ /**
+ * Retrieves the number of locks held on this script.
+ * @return the number of locks held on the previously identified script
+ */
+ int getLockers() const;
+
+ /** Sets the number of locks held on this script. */
+ void setLockers(int lockers);
+
+ /**
+ * Retrieves a pointer to the exports of this script
+ * @return pointer to the exports.
+ */
+ const uint16 *getExportTable() const { return _exportTable; }
+
+ /**
+ * Retrieves the number of exports of script.
+ * @return the number of exports of this script
+ */
+ uint16 getExportsNr() const { return _numExports; }
+
+ /**
+ * Retrieves a pointer to the synonyms associated with this script
+ * @return pointer to the synonyms, in non-parsed format.
+ */
+ const byte *getSynonyms() const { return _synonyms; }
+
+ /**
+ * Retrieves the number of synonyms associated with this script.
+ * @return the number of synonyms associated with this script
+ */
+ uint16 getSynonymsNr() const { return _numSynonyms; }
+
+ /**
+ * Validate whether the specified public function is exported by
+ * the script in the specified segment.
+ * @param pubfunct Index of the function to validate
+ * @return NULL if the public function is invalid, its
+ * offset into the script's segment otherwise
+ */
+ uint16 validateExportFunc(int pubfunct);
+
+ /**
+ * Marks the script as deleted.
+ * This will not actually delete the script. If references remain present on the
+ * heap or the stack, the script will stay in memory in a quasi-deleted state until
+ * either unreachable (resulting in its eventual deletion) or reloaded (resulting
+ * in its data being updated).
+ */
+ void markDeleted() {
+ _markedAsDeleted = true;
+ }
+
+ /**
+ * Determines whether the script is marked as being deleted.
+ */
+ bool isMarkedAsDeleted() const {
+ return _markedAsDeleted;
+ }
+
+ /**
+ * Copies a byte string into a script's heap representation.
+ * @param dst script-relative offset of the destination area
+ * @param src pointer to the data source location
+ * @param n number of bytes to copy
+ */
+ void mcpyInOut(int dst, const void *src, size_t n);
+
+ /**
+ * Finds the pointer where a block of a specific type starts from
+ */
+ byte *findBlock(int type, int skipBlockIndex = -1);
+
+private:
+ /**
+ * Processes a relocation block witin a script
+ * This function is idempotent, but it must only be called after all
+ * objects have been instantiated, or a run-time error will occur.
+ * @param obj_pos Location (segment, offset) of the block
+ * @return Location of the relocation block
+ */
+ void relocate(reg_t block);
+
+ bool relocateLocal(SegmentId segment, int location);
+};
} // End of namespace Sci
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
new file mode 100644
index 0000000000..e5fcbf72c2
--- /dev/null
+++ b/engines/sci/engine/script_patches.cpp
@@ -0,0 +1,353 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "sci/sci.h"
+#include "sci/engine/script.h"
+
+#include "common/util.h"
+
+namespace Sci {
+
+#define PATCH_END 0xFFFF
+#define PATCH_ADDTOOFFSET 0x8000
+#define PATCH_GETORIGINALBYTE 0x4000
+#define PATCH_MAGICDWORD(a, b, c, d) CONSTANT_LE_32(a | (b << 8) | (c << 16) | (d << 24))
+
+struct SciScriptSignature {
+ uint16 scriptNr;
+ const char *description;
+ uint32 magicDWord;
+ int magicOffset;
+ const byte *data;
+ const uint16 *patch;
+};
+
+// signatures are built like this:
+// - first a counter of the bytes that follow
+// - then the actual bytes that need to get matched
+// - then another counter of bytes (0 for EOS)
+// - if not EOS, an adjust offset and the actual bytes
+// - rinse and repeat
+
+
+// daySixBeignet::changeState is called when the cop goes out and sets cycles to 220.
+// this is not enough time to get to the door, so we patch that to 23 seconds
+const byte gk1SignatureDay6PoliceBeignet[] = {
+ 4,
+ 0x35, 0x04, // ldi 04
+ 0x1a, // eq?
+ 0x30, // bnt [next state check]
+ +2, 5, // [skip 2 bytes, offset of bnt]
+ 0x38, 0x93, 0x00, // pushi 93 (selector dispose)
+ 0x76, // push0
+ 0x72, // lofsa deskSarg
+ +2, 9, // [skip 2 bytes, offset of lofsa]
+ 0x4a, 0x04, 0x00, // send 04
+ 0x34, 0xdc, 0x00, // ldi 220
+ 0x65, 0x1a, // aTop cycles
+ 0x32, // jmp [end]
+ 0
+};
+
+const uint16 gk1PatchDay6PoliceBeignet[] = {
+ PATCH_ADDTOOFFSET | +16,
+ 0x34, 0x17, 0x00, // ldi 23
+ 0x65, 0x1c, // aTop seconds
+ PATCH_END
+};
+
+const byte gk1SignatureDay6PoliceSleep[] = {
+ 4,
+ 0x35, 0x08, // ldi 08
+ 0x1a, // eq?
+ 0x31, // bnt [next state check]
+ +1, 5, // [skip 1 byte, offset of bnt]
+ 0x34, 0xdc, 0x00, // ldi 220
+ 0x65, 0x1a, // aTop cycles
+ 0x32, // jmp [end]
+ 0
+};
+
+const uint16 gk1PatchDay6PoliceSleep[] = {
+ PATCH_ADDTOOFFSET | +5,
+ 0x34, 0x2a, 0x00, // ldi 42
+ 0x65, 0x1c, // aTop seconds
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature gk1Signatures[] = {
+ { 230, "day 6 police beignet timer issue", PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -16, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet },
+ { 230, "day 6 police sleep timer issue", PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -5, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep },
+ { 0, NULL, 0, 0, NULL, NULL }
+};
+
+// this here gets called on entry and when going out of game windows
+// uEvt::port will not get changed after kDisposeWindow but a bit later, so
+// we would get an invalid port handle to a kSetPort call. We just patch in
+// resetting of the port selector. We destroy the stop/fade code in there,
+// it seems it isn't used at all in the game.
+const byte hoyle4SignaturePortFix[] = {
+ 28,
+ 0x39, 0x09, // pushi 09
+ 0x89, 0x0b, // lsg 0b
+ 0x39, 0x64, // pushi 64
+ 0x38, 0xc8, 0x00, // pushi 00c8
+ 0x38, 0x2c, 0x01, // pushi 012c
+ 0x38, 0x90, 0x01, // pushi 0190
+ 0x38, 0xf4, 0x01, // pushi 01f4
+ 0x38, 0x58, 0x02, // pushi 0258
+ 0x38, 0xbc, 0x02, // pushi 02bc
+ 0x38, 0x20, 0x03, // pushi 0320
+ 0x46, // calle [xxxx] [xxxx] [xx]
+ +5, 43, // [skip 5 bytes]
+ 0x30, 0x27, 0x00, // bnt 0027 -> end of routine
+ 0x87, 0x00, // lap 00
+ 0x30, 0x19, 0x00, // bnt 0019 -> fade out
+ 0x87, 0x01, // lap 01
+ 0x30, 0x14, 0x00, // bnt 0014 -> fade out
+ 0x38, 0xa7, 0x00, // pushi 00a7
+ 0x76, // push0
+ 0x80, 0x29, 0x01, // lag 0129
+ 0x4a, 0x04, // send 04 (song::stop)
+ 0x39, 0x27, // pushi 27
+ 0x78, // push1
+ 0x8f, 0x01, // lsp 01
+ 0x51, 0x54, // class 54
+ 0x4a, 0x06, // send 06 (PlaySong::play)
+ 0x33, 0x09, // jmp 09 -> end of routine
+ 0x38, 0xaa, 0x00, // pushi 00aa
+ 0x76, // push0
+ 0x80, 0x29, 0x01, // lag 0129
+ 0x4a, 0x04, // send 04
+ 0x48, // ret
+ 0
+};
+
+const uint16 hoyle4PatchPortFix[] = {
+ PATCH_ADDTOOFFSET | +33,
+ 0x38, 0x31, 0x01, // pushi 0131 (selector curEvent)
+ 0x76, // push0
+ 0x80, 0x50, 0x00, // lag 0050 (global var 80h, "User")
+ 0x4a, 0x04, // send 04 (read User::curEvent)
+
+ 0x38, 0x93, 0x00, // pushi 0093 (selector port)
+ 0x78, // push1
+ 0x76, // push0
+ 0x4a, 0x06, // send 06 (write 0 to that object::port)
+ 0x48, // ret
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature hoyle4Signatures[] = {
+ { 0, "port fix when disposing windows", PATCH_MAGICDWORD(0x64, 0x38, 0xC8, 0x00), -5, hoyle4SignaturePortFix, hoyle4PatchPortFix },
+ { 0, NULL, 0, 0, NULL, NULL }
+};
+
+
+// this is called on every death dialog. Problem is at least the german
+// version of lsl6 gets title text that is far too long for the
+// available temp space resulting in temp space corruption
+// This patch moves the title text around, so this overflow
+// doesn't happen anymore. We would otherwise get a crash
+// calling for invalid views (this happens of course also
+// in sierra sci)
+const byte larry6SignatureDeathDialog[] = {
+ 7,
+ 0x3e, 0x33, 0x01, // link 0133 (offset 0x20)
+ 0x35, 0xff, // ldi ff
+ 0xa3, 0x00, // sal 00
+ +255, 0,
+ +255, 0,
+ +170, 12, // [skip 680 bytes]
+ 0x8f, 0x01, // lsp 01 (offset 0x2cf)
+ 0x7a, // push2
+ 0x5a, 0x04, 0x00, 0x0e, 0x01, // lea 0004 010e
+ 0x36, // push
+ 0x43, 0x7c, 0x0e, // kMessage[7c] 0e
+ +90, 10, // [skip 90 bytes]
+ 0x38, 0xd6, 0x00, // pushi 00d6 (offset 0x335)
+ 0x78, // push1
+ 0x5a, 0x04, 0x00, 0x0e, 0x01, // lea 0004 010e
+ 0x36, // push
+ +76, 11, // [skip 76 bytes]
+ 0x38, 0xcd, 0x00, // pushi 00cd (offset 0x38b)
+ 0x39, 0x03, // pushi 03
+ 0x5a, 0x04, 0x00, 0x0e, 0x01, // lea 0004 010e
+ 0x36,
+ 0
+};
+
+const uint16 larry6PatchDeathDialog[] = {
+ 0x3e, 0x00, 0x02, // link 0200
+ PATCH_ADDTOOFFSET | +687,
+ 0x5a, 0x04, 0x00, 0x40, 0x01, // lea 0004 0140
+ PATCH_ADDTOOFFSET | +98,
+ 0x5a, 0x04, 0x00, 0x40, 0x01, // lea 0004 0140
+ PATCH_ADDTOOFFSET | +82,
+ 0x5a, 0x04, 0x00, 0x40, 0x01, // lea 0004 0140
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature larry6Signatures[] = {
+ { 82, "death dialog memory corruption", PATCH_MAGICDWORD(0x3e, 0x33, 0x01, 0x35), 0, larry6SignatureDeathDialog, larry6PatchDeathDialog },
+ { 0, NULL, 0, 0, NULL, NULL }
+};
+
+// It seems to scripts warp ego outside the screen somehow (or maybe kDoBresen?)
+// ego::mover is set to 0 and rm119::doit will crash in that case. This here
+// fixes part of the problem and actually checks ego::mover to be 0 and skips
+// TODO: this should get further investigated by waltervn and maybe properly
+// patched. For now ego will shortly disappear and reappear a bit after
+// this isn't good, but sierra sci also "crashed" (endless looped) so this
+// is at least better than the original code
+const byte sq5SignatureScrubbing[] = {
+ 19,
+ 0x18, // not
+ 0x31, 0x37, // bnt 37
+ 0x78, // push1 (selector x)
+ 0x76, // push0
+ 0x39, 0x38, // pushi 38 (selector mover)
+ 0x76, // push0
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x04, // send 04 (read ego::mover)
+ 0x4a, 0x04, // send 04 (read ego::mover::x)
+ 0x36, // push
+ 0x34, 0xa0, 0x00, // ldi 00a0
+ 0x1c, // ne?
+ 0
+};
+
+const uint16 sq5PatchScrubbing[] = {
+ 0x18, // not
+ 0x31, 0x37, // bnt 37
+// 0x2f, 0x38, // bt 37 (would save another byte, isn't needed
+ 0x39, 0x38, // pushi 38 (selector mover)
+ 0x76, // push0
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x04, // send 04 (read ego::mover)
+ 0x31, 0x2e, // bnt 2e (jump if ego::mover is 0)
+ 0x78, // push1 (selector x)
+ 0x76, // push0
+ 0x4a, 0x04, // send 04 (read ego::mover::x)
+ 0x39, 0xa0, // pushi a0 (saving 2 bytes)
+ 0x1c, // ne?
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature sq5Signatures[] = {
+ { 119, "scrubbing send crash", PATCH_MAGICDWORD(0x18, 0x31, 0x37, 0x78), 0, sq5SignatureScrubbing, sq5PatchScrubbing },
+ { 0, NULL, 0, 0, NULL, NULL }
+};
+
+
+// will actually patch previously found signature area
+void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) {
+ int32 offset = signatureOffset;
+ uint16 patchWord = *patch;
+
+ while (patchWord != PATCH_END) {
+ if (patchWord & PATCH_ADDTOOFFSET) {
+ offset += patchWord & ~PATCH_ADDTOOFFSET;
+ } else if (patchWord & PATCH_GETORIGINALBYTE) {
+ // TODO: implement this
+ } else {
+ scriptData[offset] = patchWord & 0xFF;
+ offset++;
+ }
+ patch++;
+ patchWord = *patch;
+ }
+}
+
+// will return -1 if no match was found, otherwise an offset to the start of the signature match
+int32 Script::findSignature(const SciScriptSignature *signature, const byte *scriptData, const uint32 scriptSize) {
+ if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay
+ return -1;
+
+ const uint32 magicDWord = signature->magicDWord; // is platform-specific BE/LE form, so that the later match will work
+ const uint32 searchLimit = scriptSize - 3;
+ uint32 DWordOffset = 0;
+ // first search for the magic DWORD
+ while (DWordOffset < searchLimit) {
+ if (magicDWord == *(const uint32 *)(scriptData + DWordOffset)) {
+ // magic DWORD found, check if actual signature matches
+ uint32 offset = DWordOffset + signature->magicOffset;
+ uint32 byteOffset = offset;
+ const byte *signatureData = signature->data;
+ byte matchAdjust = 1;
+ while (matchAdjust) {
+ byte matchBytesCount = *signatureData++;
+ if ((byteOffset + matchBytesCount) > scriptSize) // Out-Of-Bounds?
+ break;
+ if (memcmp(signatureData, &scriptData[byteOffset], matchBytesCount)) // Byte-Mismatch?
+ break;
+ // those bytes matched, adjust offsets accordingly
+ signatureData += matchBytesCount;
+ byteOffset += matchBytesCount;
+ // get offset...
+ matchAdjust = *signatureData++;
+ byteOffset += matchAdjust;
+ }
+ if (!matchAdjust) // all matches worked?
+ return offset;
+ }
+ DWordOffset++;
+ }
+ // nothing found
+ return -1;
+}
+
+void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) {
+ const SciScriptSignature *signatureTable = NULL;
+ if (g_sci->getGameId() == GID_GK1)
+ signatureTable = gk1Signatures;
+// hoyle4 now works due workaround inside GfxPorts
+// if (g_sci->getGameId() == GID_HOYLE4)
+// signatureTable = hoyle4Signatures;
+ if (g_sci->getGameId() == GID_LSL6)
+ signatureTable = larry6Signatures;
+ if (g_sci->getGameId() == GID_SQ5)
+ signatureTable = sq5Signatures;
+
+ if (signatureTable) {
+ while (signatureTable->data) {
+ if (scriptNr == signatureTable->scriptNr) {
+ int32 foundOffset = findSignature(signatureTable, scriptData, scriptSize);
+ if (foundOffset != -1) {
+ // found, so apply the patch
+ warning("matched %s on script %d offset %d", signatureTable->description, scriptNr, foundOffset);
+ applyPatch(signatureTable->patch, scriptData, scriptSize, foundOffset);
+ }
+ }
+ signatureTable++;
+ }
+ }
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 4b60626b2e..9c08526fbb 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -63,46 +63,11 @@ const char *opcodeNames[] = {
"-sli", "-sti", "-spi"
};
-extern const char *selector_name(EngineState *s, int selector);
-
-DebugState g_debugState;
-
-int propertyOffsetToId(SegManager *segMan, int prop_ofs, reg_t objp) {
- Object *obj = segMan->getObject(objp);
- byte *selectoroffset;
- int selectors;
-
- if (!obj) {
- warning("Applied propertyOffsetToId on non-object at %04x:%04x", PRINT_REG(objp));
- return -1;
- }
-
- selectors = obj->getVarCount();
-
- if (getSciVersion() < SCI_VERSION_1_1)
- selectoroffset = ((byte *)(obj->_baseObj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2;
- else {
- if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS)) {
- obj = segMan->getObject(obj->getSuperClassSelector());
- selectoroffset = (byte *)obj->_baseVars;
- } else
- selectoroffset = (byte *)obj->_baseVars;
- }
-
- if (prop_ofs < 0 || (prop_ofs >> 1) >= selectors) {
- warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d]) on object at %04x:%04x",
- prop_ofs, prop_ofs >> 1, selectors - 1, PRINT_REG(objp));
- return -1;
- }
-
- return READ_SCI11ENDIAN_UINT16(selectoroffset + prop_ofs);
-}
-
// Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered.
reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecode) {
SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT);
Script *script_entity = NULL;
- byte *scr;
+ const byte *scr;
int scr_size;
reg_t retval = make_reg(pos.segment, pos.offset + 1);
uint16 param_value;
@@ -115,8 +80,8 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
} else
script_entity = (Script *)mobj;
- scr = script_entity->_buf;
- scr_size = script_entity->_bufSize;
+ scr = script_entity->getBuf();
+ scr_size = script_entity->getBufSize();
if (pos.offset >= scr_size) {
warning("Trying to disassemble beyond end of script");
@@ -221,51 +186,52 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
}
}
- if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode
+ if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode
if ((opcode == op_pTos) || (opcode == op_sTop) || (opcode == op_pToa) || (opcode == op_aTop) ||
(opcode == op_dpToa) || (opcode == op_ipToa) || (opcode == op_dpTos) || (opcode == op_ipTos)) {
- int prop_ofs = scr[pos.offset + 1];
- int prop_id = propertyOffsetToId(s->_segMan, prop_ofs, scriptState.xs->objp);
-
- printf(" (%s)", selector_name(s, prop_id));
+ const Object *obj = s->_segMan->getObject(s->xs->objp);
+ if (!obj)
+ warning("Attempted to reference on non-object at %04x:%04x", PRINT_REG(s->xs->objp));
+ else
+ printf(" (%s)", g_sci->getKernel()->getSelectorName(obj->propertyOffsetToId(s->_segMan, scr[pos.offset + 1])).c_str());
}
}
printf("\n");
- if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode
+ if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode
if (opcode == op_callk) {
- int stackframe = (scr[pos.offset + 2] >> 1) + (scriptState.restAdjust);
- int argc = ((scriptState.xs->sp)[- stackframe - 1]).offset;
+ int stackframe = (scr[pos.offset + 2] >> 1) + (s->restAdjust);
+ int argc = ((s->xs->sp)[- stackframe - 1]).offset;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
if (!oldScriptHeader)
- argc += (scriptState.restAdjust);
+ argc += (s->restAdjust);
printf(" Kernel params: (");
for (int j = 0; j < argc; j++) {
- printf("%04x:%04x", PRINT_REG((scriptState.xs->sp)[j - stackframe]));
+ printf("%04x:%04x", PRINT_REG((s->xs->sp)[j - stackframe]));
if (j + 1 < argc)
printf(", ");
}
printf(")\n");
} else if ((opcode == op_send) || (opcode == op_self)) {
- int restmod = scriptState.restAdjust;
+ int restmod = s->restAdjust;
int stackframe = (scr[pos.offset + 1] >> 1) + restmod;
- reg_t *sb = scriptState.xs->sp;
+ reg_t *sb = s->xs->sp;
uint16 selector;
reg_t fun_ref;
while (stackframe > 0) {
int argc = sb[- stackframe + 1].offset;
const char *name = NULL;
- reg_t called_obj_addr = scriptState.xs->objp;
+ reg_t called_obj_addr = s->xs->objp;
if (opcode == op_send)
called_obj_addr = s->r_acc;
else if (opcode == op_self)
- called_obj_addr = scriptState.xs->objp;
+ called_obj_addr = s->xs->objp;
selector = sb[- stackframe].offset;
@@ -274,9 +240,9 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
if (!name)
name = "<invalid>";
- printf(" %s::%s[", name, (selector > kernel->getSelectorNamesSize()) ? "<invalid>" : selector_name(s, selector));
+ printf(" %s::%s[", name, g_sci->getKernel()->getSelectorName(selector).c_str());
- switch (lookup_selector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) {
+ switch (lookupSelector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) {
case kSelectorMethod:
printf("FUNCT");
argc += restmod;
@@ -309,90 +275,76 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
}
-void script_debug(EngineState *s) {
- // Do we support a separate console?
+void SciEngine::scriptDebug() {
+ EngineState *s = _gamestate;
+ if (_debugState.seeking && !_debugState.breakpointWasHit) { // Are we looking for something special?
+ if (_debugState.seeking == kDebugSeekStepOver) {
+ // are we above seek-level? resume then
+ if (_debugState.seekLevel < (int)s->_executionStack.size())
+ return;
+ _debugState.seeking = kDebugSeekNothing;
+ }
-#if 0
- if (sci_debug_flags & _DEBUG_FLAG_LOGGING) {
- printf("%d: acc=%04x:%04x ", script_step_counter, PRINT_REG(s->r_acc));
- disassemble(s, scriptState.xs->addr.pc, 0, 1);
- if (scriptState.seeking == kDebugSeekGlobal)
- printf("Global %d (0x%x) = %04x:%04x\n", scriptState.seekSpecial,
- scriptState.seekSpecial, PRINT_REG(s->script_000->_localsBlock->_locals[scriptState.seekSpecial]));
- }
-#endif
+ if (_debugState.seeking != kDebugSeekNothing) {
+ const reg_t pc = s->xs->addr.pc;
+ SegmentObj *mobj = s->_segMan->getSegment(pc.segment, SEG_TYPE_SCRIPT);
+
+ if (mobj) {
+ Script *scr = (Script *)mobj;
+ const byte *code_buf = scr->getBuf();
+ int code_buf_size = scr->getBufSize();
+ int opcode = pc.offset >= code_buf_size ? 0 : code_buf[pc.offset];
+ int op = opcode >> 1;
+ int paramb1 = pc.offset + 1 >= code_buf_size ? 0 : code_buf[pc.offset + 1];
+ int paramf1 = (opcode & 1) ? paramb1 : (pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + pc.offset + 1));
+
+ switch (_debugState.seeking) {
+ case kDebugSeekSpecialCallk:
+ if (paramb1 != _debugState.seekSpecial)
+ return;
+
+ case kDebugSeekCallk:
+ if (op != op_callk)
+ return;
+ break;
-#if 0
- if (!g_debugState.debugging)
- return;
-#endif
-
- if (g_debugState.seeking && !g_debugState.breakpointWasHit) { // Are we looking for something special?
- SegmentObj *mobj = s->_segMan->getSegment(scriptState.xs->addr.pc.segment, SEG_TYPE_SCRIPT);
-
- if (mobj) {
- Script *scr = (Script *)mobj;
- byte *code_buf = scr->_buf;
- int code_buf_size = scr->_bufSize;
- int opcode = scriptState.xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset];
- int op = opcode >> 1;
- int paramb1 = scriptState.xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset + 1];
- int paramf1 = (opcode & 1) ? paramb1 : (scriptState.xs->addr.pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + scriptState.xs->addr.pc.offset + 1));
-
- switch (g_debugState.seeking) {
- case kDebugSeekSpecialCallk:
- if (paramb1 != g_debugState.seekSpecial)
- return;
-
- case kDebugSeekCallk: {
- if (op != op_callk)
- return;
- break;
- }
+ case kDebugSeekLevelRet:
+ if ((op != op_ret) || (_debugState.seekLevel < (int)s->_executionStack.size()-1))
+ return;
+ break;
- case kDebugSeekLevelRet: {
- if ((op != op_ret) || (g_debugState.seekLevel < (int)s->_executionStack.size()-1))
- return;
- break;
- }
+ case kDebugSeekGlobal:
+ if (op < op_sag)
+ return;
+ if ((op & 0x3) > 1)
+ return; // param or temp
+ if ((op & 0x3) && s->_executionStack.back().local_segment > 0)
+ return; // locals and not running in script.000
+ if (paramf1 != _debugState.seekSpecial)
+ return; // CORRECT global?
+ break;
- case kDebugSeekGlobal:
- if (op < op_sag)
- return;
- if ((op & 0x3) > 1)
- return; // param or temp
- if ((op & 0x3) && s->_executionStack.back().local_segment > 0)
- return; // locals and not running in script.000
- if (paramf1 != g_debugState.seekSpecial)
- return; // CORRECT global?
- break;
-
- case kDebugSeekSO:
- // FIXME: Unhandled?
- break;
-
- case kDebugSeekNothing:
- // We seek nothing, so just continue
- break;
- }
+ default:
+ break;
+ }
- g_debugState.seeking = kDebugSeekNothing;
- // OK, found whatever we were looking for
+ _debugState.seeking = kDebugSeekNothing;
+ }
}
+ // OK, found whatever we were looking for
}
- printf("Step #%d\n", script_step_counter);
- disassemble(s, scriptState.xs->addr.pc, 0, 1);
+ printf("Step #%d\n", s->scriptStepCounter);
+ disassemble(s, s->xs->addr.pc, 0, 1);
- if (g_debugState.runningStep) {
- g_debugState.runningStep--;
+ if (_debugState.runningStep) {
+ _debugState.runningStep--;
return;
}
- g_debugState.debugging = false;
+ _debugState.debugging = false;
- Console *con = ((Sci::SciEngine*)g_engine)->getSciDebugger();
- con->attach();
+ _console->attach();
}
void Kernel::dumpScriptObject(char *data, int seeker, int objsize) {
@@ -521,106 +473,57 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
dumpScriptObject((char *)script->data, seeker, objsize);
break;
- case SCI_OBJ_CODE: {
+ case SCI_OBJ_CODE:
printf("Code\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case 3: {
+ case 3:
printf("<unknown>\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case SCI_OBJ_SAID: {
+ case SCI_OBJ_SAID:
printf("Said\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
printf("%04x: ", seeker);
- while (seeker < _seeker) {
- unsigned char nextitem = script->data [seeker++];
- if (nextitem == 0xFF)
- printf("\n%04x: ", seeker);
- else if (nextitem >= 0xF0) {
- switch (nextitem) {
- case 0xf0:
- printf(", ");
- break;
- case 0xf1:
- printf("& ");
- break;
- case 0xf2:
- printf("/ ");
- break;
- case 0xf3:
- printf("( ");
- break;
- case 0xf4:
- printf(") ");
- break;
- case 0xf5:
- printf("[ ");
- break;
- case 0xf6:
- printf("] ");
- break;
- case 0xf7:
- printf("# ");
- break;
- case 0xf8:
- printf("< ");
- break;
- case 0xf9:
- printf("> ");
- break;
- }
- } else {
- nextitem = nextitem << 8 | script->data [seeker++];
- printf("%s[%03x] ", vocab->getAnyWordFromGroup(nextitem), nextitem);
- }
- }
+ vocab->debugDecipherSaidBlock(script->data + seeker);
printf("\n");
- }
- break;
+ break;
- case SCI_OBJ_STRINGS: {
+ case SCI_OBJ_STRINGS:
printf("Strings\n");
while (script->data [seeker]) {
printf("%04x: %s\n", seeker, script->data + seeker);
seeker += strlen((char *)script->data + seeker) + 1;
}
seeker++; // the ending zero byte
- };
- break;
+ break;
case SCI_OBJ_CLASS:
dumpScriptClass((char *)script->data, seeker, objsize);
break;
- case SCI_OBJ_EXPORTS: {
+ case SCI_OBJ_EXPORTS:
printf("Exports\n");
Common::hexdump((unsigned char *)script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case SCI_OBJ_POINTERS: {
+ case SCI_OBJ_POINTERS:
printf("Pointers\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case 9: {
+ case 9:
printf("<unknown>\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case SCI_OBJ_LOCALVARS: {
+ case SCI_OBJ_LOCALVARS:
printf("Local vars\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
default:
printf("Unsupported!\n");
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index b18d76e1a7..1fb37f458d 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -26,6 +26,7 @@
#include "sci/sci.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
+#include "sci/engine/script.h"
namespace Sci {
@@ -39,14 +40,14 @@ enum {
SegManager::SegManager(ResourceManager *resMan) {
_heap.push_back(0);
- Clones_seg_id = 0;
- Lists_seg_id = 0;
- Nodes_seg_id = 0;
- Hunks_seg_id = 0;
+ _clonesSegId = 0;
+ _listsSegId = 0;
+ _nodesSegId = 0;
+ _hunksSegId = 0;
#ifdef ENABLE_SCI32
- Arrays_seg_id = 0;
- String_seg_id = 0;
+ _arraysSegId = 0;
+ _stringSegId = 0;
#endif
_resMan = resMan;
@@ -54,7 +55,6 @@ SegManager::SegManager(ResourceManager *resMan) {
createClassTable();
}
-// Destroy the object, free the memorys if allocated before
SegManager::~SegManager() {
resetSegMan();
}
@@ -71,27 +71,40 @@ void SegManager::resetSegMan() {
// And reinitialize
_heap.push_back(0);
- Clones_seg_id = 0;
- Lists_seg_id = 0;
- Nodes_seg_id = 0;
- Hunks_seg_id = 0;
+ _clonesSegId = 0;
+ _listsSegId = 0;
+ _nodesSegId = 0;
+ _hunksSegId = 0;
// Reinitialize class table
- _classtable.clear();
+ _classTable.clear();
createClassTable();
}
+void SegManager::initSysStrings() {
+ _sysStrings = (SystemStrings *)allocSegment(new SystemStrings(), &_sysStringsSegId);
+
+ // Allocate static buffer for savegame and CWD directories
+ SystemString *strSaveDir = getSystemString(SYS_STRING_SAVEDIR);
+ strSaveDir->_name = "savedir";
+ strSaveDir->_maxSize = MAX_SAVE_DIR_SIZE;
+ strSaveDir->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char));
+ // Set the savegame dir (actually, we set it to a fake value,
+ // since we cannot let the game control where saves are stored)
+ ::strcpy(strSaveDir->_value, "");
+
+ // Allocate static buffer for the parser base
+ SystemString *strParserBase = getSystemString(SYS_STRING_PARSER_BASE);
+ strParserBase->_name = "parser-base";
+ strParserBase->_maxSize = MAX_PARSER_BASE;
+ strParserBase->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char));
+}
+
SegmentId SegManager::findFreeSegment() const {
- // FIXME: This is a very crude approach: We find a free segment id by scanning
- // from the start. This can be slow if the number of segments becomes large.
- // Optimizations are possible and easy, but I refrain from doing any until this
- // refactoring is complete.
- // One very simple yet probably effective approach would be to add
- // "int firstFreeSegment", which is updated when allocating/freeing segments.
- // There are some scenarios where it performs badly, but we should first check
- // whether those occur at all. If so, there are easy refinements that deal
- // with this. Finally, we could switch to a more elaborate scheme,
- // such as keeping track of all free segments. But I doubt this is necessary.
+ // The following is a very crude approach: We find a free segment id by
+ // scanning from the start. This can be slow if the number of segments
+ // becomes large. Optimizations are possible and easy, but I'll refrain
+ // from attempting any until we determine we actually need it.
uint seg = 1;
while (seg < _heap.size() && _heap[seg]) {
++seg;
@@ -144,7 +157,7 @@ int SegManager::deallocate(SegmentId seg, bool recursive) {
if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
- _scriptSegMap.erase(scr->_nr);
+ _scriptSegMap.erase(scr->getScriptNumber());
if (recursive && scr->_localsSegment)
deallocate(scr->_localsSegment, recursive);
}
@@ -155,12 +168,12 @@ int SegManager::deallocate(SegmentId seg, bool recursive) {
return 1;
}
-bool SegManager::isHeapObject(reg_t pos) {
- Object *obj = getObject(pos);
+bool SegManager::isHeapObject(reg_t pos) const {
+ const Object *obj = getObject(pos);
if (obj == NULL || (obj && obj->isFreed()))
return false;
Script *scr = getScriptIfLoaded(pos.segment);
- return !(scr && scr->_markedAsDeleted);
+ return !(scr && scr->isMarkedAsDeleted());
}
void SegManager::deallocateScript(int script_nr) {
@@ -181,36 +194,36 @@ Script *SegManager::getScript(const SegmentId seg) {
return (Script *)_heap[seg];
}
-Script *SegManager::getScriptIfLoaded(const SegmentId seg) {
+Script *SegManager::getScriptIfLoaded(const SegmentId seg) const {
if (seg < 1 || (uint)seg >= _heap.size() || !_heap[seg] || _heap[seg]->getType() != SEG_TYPE_SCRIPT)
return 0;
return (Script *)_heap[seg];
}
-SegmentId SegManager::findSegmentByType(int type) {
+SegmentId SegManager::findSegmentByType(int type) const {
for (uint i = 0; i < _heap.size(); i++)
if (_heap[i] && _heap[i]->getType() == type)
return i;
return 0;
}
-SegmentObj *SegManager::getSegmentObj(SegmentId seg) {
+SegmentObj *SegManager::getSegmentObj(SegmentId seg) const {
if (seg < 1 || (uint)seg >= _heap.size() || !_heap[seg])
return 0;
return _heap[seg];
}
-SegmentType SegManager::getSegmentType(SegmentId seg) {
+SegmentType SegManager::getSegmentType(SegmentId seg) const {
if (seg < 1 || (uint)seg >= _heap.size() || !_heap[seg])
return SEG_TYPE_INVALID;
return _heap[seg]->getType();
}
-SegmentObj *SegManager::getSegment(SegmentId seg, SegmentType type) {
+SegmentObj *SegManager::getSegment(SegmentId seg, SegmentType type) const {
return getSegmentType(seg) == type ? _heap[seg] : NULL;
}
-Object *SegManager::getObject(reg_t pos) {
+Object *SegManager::getObject(reg_t pos) const {
SegmentObj *mobj = getSegmentObj(pos.segment);
Object *obj = NULL;
@@ -223,8 +236,8 @@ Object *SegManager::getObject(reg_t pos) {
warning("getObject(): Trying to get an invalid object");
} else if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
- if (pos.offset <= scr->_bufSize && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
- && RAW_IS_OBJECT(scr->_buf + pos.offset)) {
+ if (pos.offset <= scr->getBufSize() && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
+ && RAW_IS_OBJECT(scr->getBuf(pos.offset))) {
obj = scr->getObject(pos.offset);
}
}
@@ -234,7 +247,7 @@ Object *SegManager::getObject(reg_t pos) {
}
const char *SegManager::getObjectName(reg_t pos) {
- Object *obj = getObject(pos);
+ const Object *obj = getObject(pos);
if (!obj)
return "<no such object>";
@@ -250,78 +263,55 @@ const char *SegManager::getObjectName(reg_t pos) {
}
reg_t SegManager::findObjectByName(const Common::String &name, int index) {
- reg_t retVal = NULL_REG;
+ Common::Array<reg_t> result;
+ uint i;
// Now all values are available; iterate over all objects.
- int timesFound = 0;
- for (uint i = 0; i < _heap.size(); i++) {
- SegmentObj *mobj = _heap[i];
- int idx = 0;
- int max_index = 0;
- ObjMap::iterator it;
- Script *scr = 0;
- CloneTable *ct = 0;
-
- if (mobj) {
- if (mobj->getType() == SEG_TYPE_SCRIPT) {
- scr = (Script *)mobj;
- max_index = scr->_objects.size();
- it = scr->_objects.begin();
- } else if (mobj->getType() == SEG_TYPE_CLONES) {
- ct = (CloneTable *)mobj;
- max_index = ct->_table.size();
- }
- }
+ for (i = 0; i < _heap.size(); i++) {
+ const SegmentObj *mobj = _heap[i];
+
+ if (!mobj)
+ continue;
- // It's a script or a clone table, scan all objects in it
- for (; idx < max_index; ++idx) {
- Object *obj = NULL;
- reg_t objpos;
- objpos.offset = 0;
- objpos.segment = i;
-
- if (mobj->getType() == SEG_TYPE_SCRIPT) {
- obj = &(it->_value);
- objpos.offset = obj->getPos().offset;
- ++it;
- } else if (mobj->getType() == SEG_TYPE_CLONES) {
+ reg_t objpos = make_reg(i, 0);
+
+ if (mobj->getType() == SEG_TYPE_SCRIPT) {
+ // It's a script, scan all objects in it
+ const Script *scr = (const Script *)mobj;
+ for (ObjMap::const_iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ objpos.offset = it->_value.getPos().offset;
+ if (name == getObjectName(objpos))
+ result.push_back(objpos);
+ }
+ } else if (mobj->getType() == SEG_TYPE_CLONES) {
+ // It's clone table, scan all objects in it
+ const CloneTable *ct = (const CloneTable *)mobj;
+ for (uint idx = 0; idx < ct->_table.size(); ++idx) {
if (!ct->isValidEntry(idx))
continue;
- obj = &(ct->_table[idx]);
- objpos.offset = idx;
- }
- const char *objname = getObjectName(objpos);
- if (name == objname) {
- // Found a match!
- if ((index < 0) && (timesFound > 0)) {
- if (timesFound == 1) {
- // First time we realized the ambiguity
- printf("Ambiguous:\n");
- printf(" %3x: [%04x:%04x] %s\n", 0, PRINT_REG(retVal), name.c_str());
- }
- printf(" %3x: [%04x:%04x] %s\n", timesFound, PRINT_REG(objpos), name.c_str());
- }
- if (index < 0 || timesFound == index)
- retVal = objpos;
- ++timesFound;
+ objpos.offset = idx;
+ if (name == getObjectName(objpos))
+ result.push_back(objpos);
}
}
-
}
- if (!timesFound)
+ if (result.empty())
return NULL_REG;
- if (timesFound > 1 && index < 0) {
- printf("Ambiguous: Aborting.\n");
+ if (result.size() > 1 && index < 0) {
+ printf("Ambiguous:\n");
+ for (i = 0; i < result.size(); i++)
+ printf(" %3x: [%04x:%04x] %s\n", i, PRINT_REG(result[i]), name.c_str());
return NULL_REG; // Ambiguous
}
- if (timesFound <= index)
+ if (index < 0)
+ return result[0];
+ else if (result.size() <= (uint)index)
return NULL_REG; // Not found
-
- return retVal;
+ return result[index];
}
// validate the seg
@@ -348,7 +338,7 @@ SegmentId SegManager::getScriptSegment(int script_nr, ScriptLoadType load) {
SegmentId segment;
if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD)
- script_instantiate(_resMan, this, script_nr);
+ instantiateScript(script_nr);
segment = getScriptSegment(script_nr);
@@ -359,8 +349,8 @@ SegmentId SegManager::getScriptSegment(int script_nr, ScriptLoadType load) {
return segment;
}
-LocalVariables *SegManager::allocLocalsSegment(Script *scr, int count) {
- if (!count) { // No locals
+LocalVariables *SegManager::allocLocalsSegment(Script *scr) {
+ if (!scr->getLocalsCount()) { // No locals
scr->_localsSegment = 0;
scr->_localsBlock = NULL;
return NULL;
@@ -371,13 +361,13 @@ LocalVariables *SegManager::allocLocalsSegment(Script *scr, int count) {
locals = (LocalVariables *)_heap[scr->_localsSegment];
VERIFY(locals != NULL, "Re-used locals segment was NULL'd out");
VERIFY(locals->getType() == SEG_TYPE_LOCALS, "Re-used locals segment did not consist of local variables");
- VERIFY(locals->script_id == scr->_nr, "Re-used locals segment belonged to other script");
+ VERIFY(locals->script_id == scr->getScriptNumber(), "Re-used locals segment belonged to other script");
} else
locals = (LocalVariables *)allocSegment(new LocalVariables(), &scr->_localsSegment);
scr->_localsBlock = locals;
- locals->script_id = scr->_nr;
- locals->_locals.resize(count);
+ locals->script_id = scr->getScriptNumber();
+ locals->_locals.resize(scr->getLocalsCount());
return locals;
}
@@ -390,11 +380,13 @@ DataStack *SegManager::allocateStack(int size, SegmentId *segid) {
retval->_entries = (reg_t *)calloc(size, sizeof(reg_t));
retval->_capacity = size;
- return retval;
-}
+ // SSCI initializes the stack with "S" characters (uppercase S in SCI0-SCI1,
+ // lowercase s in SCI0 and SCI11) - probably stands for "stack"
+ byte filler = (getSciVersion() >= SCI_VERSION_01 && getSciVersion() <= SCI_VERSION_1_LATE) ? 'S' : 's';
+ for (int i = 0; i < size; i++)
+ retval->_entries[i] = make_reg(0, filler);
-SystemStrings *SegManager::allocateSysStrings(SegmentId *segid) {
- return (SystemStrings *)allocSegment(new SystemStrings(), segid);
+ return retval;
}
void SegManager::freeHunkEntry(reg_t addr) {
@@ -417,13 +409,13 @@ reg_t SegManager::allocateHunkEntry(const char *hunk_type, int size) {
HunkTable *table;
int offset;
- if (!Hunks_seg_id)
- allocSegment(new HunkTable(), &(Hunks_seg_id));
- table = (HunkTable *)_heap[Hunks_seg_id];
+ if (!_hunksSegId)
+ allocSegment(new HunkTable(), &(_hunksSegId));
+ table = (HunkTable *)_heap[_hunksSegId];
offset = table->allocEntry();
- reg_t addr = make_reg(Hunks_seg_id, offset);
+ reg_t addr = make_reg(_hunksSegId, offset);
Hunk *h = &(table->_table[offset]);
if (!h)
@@ -440,7 +432,7 @@ byte *SegManager::getHunkPointer(reg_t addr) {
HunkTable *ht = (HunkTable *)getSegment(addr.segment, SEG_TYPE_HUNK);
if (!ht || !ht->isValidEntry(addr.offset)) {
- warning("getHunkPointer() with invalid handle");
+ // Valid SCI behavior, e.g. when loading/quitting
return NULL;
}
@@ -451,61 +443,28 @@ Clone *SegManager::allocateClone(reg_t *addr) {
CloneTable *table;
int offset;
- if (!Clones_seg_id)
- table = (CloneTable *)allocSegment(new CloneTable(), &(Clones_seg_id));
+ if (!_clonesSegId)
+ table = (CloneTable *)allocSegment(new CloneTable(), &(_clonesSegId));
else
- table = (CloneTable *)_heap[Clones_seg_id];
+ table = (CloneTable *)_heap[_clonesSegId];
offset = table->allocEntry();
- *addr = make_reg(Clones_seg_id, offset);
+ *addr = make_reg(_clonesSegId, offset);
return &(table->_table[offset]);
}
-void SegManager::reconstructClones() {
- for (uint i = 0; i < _heap.size(); i++) {
- if (_heap[i]) {
- SegmentObj *mobj = _heap[i];
- if (mobj->getType() == SEG_TYPE_CLONES) {
- CloneTable *ct = (CloneTable *)mobj;
-
- for (uint j = 0; j < ct->_table.size(); j++) {
- // Check if the clone entry is used
- uint entryNum = (uint)ct->first_free;
- bool isUsed = true;
- while (entryNum != ((uint) CloneTable::HEAPENTRY_INVALID)) {
- if (entryNum == j) {
- isUsed = false;
- break;
- }
- entryNum = ct->_table[entryNum].next_free;
- }
-
- if (!isUsed)
- continue;
-
- CloneTable::Entry &seeker = ct->_table[j];
- Object *baseObj = getObject(seeker.getSpeciesSelector());
- seeker.cloneFromObject(baseObj);
- if (!baseObj)
- warning("Clone entry without a base class: %d", j);
- } // end for
- } // end if
- } // end if
- } // end for
-}
-
List *SegManager::allocateList(reg_t *addr) {
ListTable *table;
int offset;
- if (!Lists_seg_id)
- allocSegment(new ListTable(), &(Lists_seg_id));
- table = (ListTable *)_heap[Lists_seg_id];
+ if (!_listsSegId)
+ allocSegment(new ListTable(), &(_listsSegId));
+ table = (ListTable *)_heap[_listsSegId];
offset = table->allocEntry();
- *addr = make_reg(Lists_seg_id, offset);
+ *addr = make_reg(_listsSegId, offset);
return &(table->_table[offset]);
}
@@ -513,48 +472,60 @@ Node *SegManager::allocateNode(reg_t *addr) {
NodeTable *table;
int offset;
- if (!Nodes_seg_id)
- allocSegment(new NodeTable(), &(Nodes_seg_id));
- table = (NodeTable *)_heap[Nodes_seg_id];
+ if (!_nodesSegId)
+ allocSegment(new NodeTable(), &(_nodesSegId));
+ table = (NodeTable *)_heap[_nodesSegId];
offset = table->allocEntry();
- *addr = make_reg(Nodes_seg_id, offset);
+ *addr = make_reg(_nodesSegId, offset);
return &(table->_table[offset]);
}
+reg_t SegManager::newNode(reg_t value, reg_t key) {
+ reg_t nodeRef;
+ Node *n = allocateNode(&nodeRef);
+ n->pred = n->succ = NULL_REG;
+ n->key = key;
+ n->value = value;
+
+ return nodeRef;
+}
+
List *SegManager::lookupList(reg_t addr) {
if (getSegmentType(addr.segment) != SEG_TYPE_LISTS) {
- warning("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
+ error("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
return NULL;
}
ListTable *lt = (ListTable *)_heap[addr.segment];
if (!lt->isValidEntry(addr.offset)) {
- warning("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
+ error("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
return NULL;
}
return &(lt->_table[addr.offset]);
}
-Node *SegManager::lookupNode(reg_t addr) {
+Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) {
if (addr.isNull())
return NULL; // Non-error null
- if (getSegmentType(addr.segment) != SEG_TYPE_NODES) {
- // FIXME: This occurs right at the beginning of SQ4, when walking north from the first screen. It doesn't
- // seem to have any apparent ill-effects, though, so it's been changed to non-fatal, for now
- //error("%s, L%d: Attempt to use non-node %04x:%04x as list node", __FILE__, __LINE__, PRINT_REG(addr));
- warning("Attempt to use non-node %04x:%04x as list node", PRINT_REG(addr));
+ SegmentType type = getSegmentType(addr.segment);
+
+ if (type != SEG_TYPE_NODES) {
+ error("Attempt to use non-node %04x:%04x (type %d) as list node", PRINT_REG(addr), type);
return NULL;
}
NodeTable *nt = (NodeTable *)_heap[addr.segment];
if (!nt->isValidEntry(addr.offset)) {
- warning("Attempt to use non-node %04x:%04x as list node", PRINT_REG(addr));
+ if (!stopOnDiscarded)
+ return NULL;
+
+ error("Attempt to use invalid or discarded reference %04x:%04x as list node", PRINT_REG(addr));
return NULL;
}
@@ -582,7 +553,7 @@ static void *derefPtr(SegManager *segMan, reg_t pointer, int entries, bool wantR
if (ret.isRaw != wantRaw) {
warning("Dereferencing pointer %04x:%04x (type %d) which is %s, but expected %s", PRINT_REG(pointer),
- segMan->_heap[pointer.segment]->getType(),
+ segMan->getSegmentType(pointer.segment),
ret.isRaw ? "raw" : "not raw",
wantRaw ? "raw" : "not raw");
}
@@ -622,8 +593,12 @@ static inline char getChar(const SegmentRef &ref, uint offset) {
reg_t val = ref.reg[offset / 2];
+ // segment 0xFFFF means that the scripts are using uninitialized temp-variable space
+ // we can safely ignore this, if it isn't one of the first 2 chars.
+ // foreign lsl3 uses kFileIO(readraw) and then immediately uses kReadNumber right at the start
if (val.segment != 0)
- warning("Attempt to read character from non-raw data");
+ if (!((val.segment == 0xFFFF) && (offset > 1)))
+ warning("Attempt to read character from non-raw data");
return (offset & 1 ? val.offset >> 8 : val.offset & 0xff);
}
@@ -671,6 +646,14 @@ void SegManager::strncpy(reg_t dest, const char* src, size_t n) {
}
void SegManager::strncpy(reg_t dest, reg_t src, size_t n) {
+ if (src.isNull()) {
+ // Clear target string instead.
+ if (n > 0)
+ strcpy(dest, "");
+
+ return; // empty text
+ }
+
SegmentRef dest_r = dereference(dest);
const SegmentRef src_r = dereference(src);
if (!src_r.isValid()) {
@@ -798,6 +781,9 @@ void SegManager::memcpy(byte *dest, reg_t src, size_t n) {
}
size_t SegManager::strlen(reg_t str) {
+ if (str.isNull())
+ return 0; // empty text
+
SegmentRef str_r = dereference(str);
if (!str_r.isValid()) {
warning("Attempt to call strlen on invalid pointer %04x:%04x", PRINT_REG(str));
@@ -817,6 +803,9 @@ size_t SegManager::strlen(reg_t str) {
Common::String SegManager::getString(reg_t pointer, int entries) {
Common::String ret;
+ if (pointer.isNull())
+ return ret; // empty text
+
SegmentRef src_r = dereference(pointer);
if (!src_r.isValid()) {
warning("SegManager::getString(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
@@ -843,7 +832,6 @@ Common::String SegManager::getString(reg_t pointer, int entries) {
return ret;
}
-
byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
SegmentId seg;
SegmentObj *mobj = allocSegment(new DynMem(), &seg);
@@ -863,13 +851,13 @@ byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
return (byte *)(d._buf);
}
-int SegManager::freeDynmem(reg_t addr) {
+bool SegManager::freeDynmem(reg_t addr) {
if (addr.segment < 1 || addr.segment >= _heap.size() || !_heap[addr.segment] || _heap[addr.segment]->getType() != SEG_TYPE_DYNMEM)
- return 1; // error
+ return false; // error
deallocate(addr.segment, true);
- return 0; // OK
+ return true; // OK
}
#ifdef ENABLE_SCI32
@@ -877,14 +865,14 @@ SciArray<reg_t> *SegManager::allocateArray(reg_t *addr) {
ArrayTable *table;
int offset;
- if (!Arrays_seg_id) {
- table = (ArrayTable *)allocSegment(new ArrayTable(), &(Arrays_seg_id));
+ if (!_arraysSegId) {
+ table = (ArrayTable *)allocSegment(new ArrayTable(), &(_arraysSegId));
} else
- table = (ArrayTable *)_heap[Arrays_seg_id];
+ table = (ArrayTable *)_heap[_arraysSegId];
offset = table->allocEntry();
- *addr = make_reg(Arrays_seg_id, offset);
+ *addr = make_reg(_arraysSegId, offset);
return &(table->_table[offset]);
}
@@ -917,14 +905,14 @@ SciString *SegManager::allocateString(reg_t *addr) {
StringTable *table;
int offset;
- if (!String_seg_id) {
- table = (StringTable *)allocSegment(new StringTable(), &(String_seg_id));
+ if (!_stringSegId) {
+ table = (StringTable *)allocSegment(new StringTable(), &(_stringSegId));
} else
- table = (StringTable *)_heap[String_seg_id];
+ table = (StringTable *)_heap[_stringSegId];
offset = table->allocEntry();
- *addr = make_reg(String_seg_id, offset);
+ *addr = make_reg(_stringSegId, offset);
return &(table->_table[offset]);
}
@@ -955,4 +943,148 @@ void SegManager::freeString(reg_t addr) {
#endif
+void SegManager::createClassTable() {
+ Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1);
+
+ if (!vocab996)
+ error("SegManager: failed to open vocab 996");
+
+ int totalClasses = vocab996->size >> 2;
+ _classTable.resize(totalClasses);
+
+ for (uint16 classNr = 0; classNr < totalClasses; classNr++) {
+ uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2);
+
+ _classTable[classNr].reg = NULL_REG;
+ _classTable[classNr].script = scriptNr;
+ }
+
+ _resMan->unlockResource(vocab996);
+}
+
+reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller) {
+ if (classnr == 0xffff)
+ return NULL_REG;
+
+ if (classnr < 0 || (int)_classTable.size() <= classnr || _classTable[classnr].script < 0) {
+ error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classTable.size());
+ return NULL_REG;
+ } else {
+ Class *the_class = &_classTable[classnr];
+ if (!the_class->reg.segment) {
+ getScriptSegment(the_class->script, lock);
+
+ if (!the_class->reg.segment) {
+ error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
+ return NULL_REG;
+ }
+ } else
+ if (caller.segment != the_class->reg.segment)
+ getScript(the_class->reg.segment)->incrementLockers();
+
+ return the_class->reg;
+ }
+}
+
+int SegManager::instantiateScript(int scriptNum) {
+ SegmentId segmentId = getScriptSegment(scriptNum);
+ Script *scr = getScriptIfLoaded(segmentId);
+ if (scr) {
+ if (!scr->isMarkedAsDeleted()) {
+ scr->incrementLockers();
+ return segmentId;
+ } else {
+ scr->freeScript();
+ }
+ } else {
+ scr = allocateScript(scriptNum, &segmentId);
+ }
+
+ scr->init(scriptNum, _resMan);
+ scr->load(_resMan);
+ scr->initialiseLocals(this);
+ scr->initialiseClasses(this);
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ scr->initialiseObjectsSci11(this, segmentId);
+ } else {
+ scr->initialiseObjectsSci0(this, segmentId);
+ }
+
+ return segmentId;
+}
+
+void SegManager::uninstantiateScript(int script_nr) {
+ SegmentId segmentId = getScriptSegment(script_nr);
+ Script *scr = getScriptIfLoaded(segmentId);
+
+ if (!scr) { // Is it already unloaded?
+ //warning("unloading script 0x%x requested although not loaded", script_nr);
+ // This is perfectly valid SCI behaviour
+ return;
+ }
+
+ scr->decrementLockers(); // One less locker
+
+ if (scr->getLockers() > 0)
+ return;
+
+ // Free all classtable references to this script
+ for (uint i = 0; i < classTableSize(); i++)
+ if (getClass(i).reg.segment == segmentId)
+ setClassOffset(i, NULL_REG);
+
+ if (getSciVersion() < SCI_VERSION_1_1)
+ uninstantiateScriptSci0(script_nr);
+ // FIXME: Add proper script uninstantiation for SCI 1.1
+
+ if (!scr->getLockers()) {
+ // The actual script deletion seems to be done by SCI scripts themselves
+ scr->markDeleted();
+ debugC(kDebugLevelScripts, "Unloaded script 0x%x.", script_nr);
+ }
+}
+
+void SegManager::uninstantiateScriptSci0(int script_nr) {
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+ SegmentId segmentId = getScriptSegment(script_nr);
+ Script *scr = getScript(segmentId);
+ reg_t reg = make_reg(segmentId, oldScriptHeader ? 2 : 0);
+ int objType, objLength = 0;
+
+ // Make a pass over the object in order uninstantiate all superclasses
+
+ do {
+ reg.offset += objLength; // Step over the last checked object
+
+ objType = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset));
+ if (!objType)
+ break;
+ objLength = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset + 2));
+
+ reg.offset += 4; // Step over header
+
+ if ((objType == SCI_OBJ_OBJECT) || (objType == SCI_OBJ_CLASS)) { // object or class?
+ reg.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
+ int16 superclass = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset + 2));
+
+ if (superclass >= 0) {
+ int superclass_script = getClass(superclass).script;
+
+ if (superclass_script == script_nr) {
+ if (scr->getLockers())
+ scr->decrementLockers(); // Decrease lockers if this is us ourselves
+ } else
+ uninstantiateScript(superclass_script);
+ // Recurse to assure that the superclass lockers number gets decreased
+ }
+
+ reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
+ } // if object or class
+
+ reg.offset -= 4; // Step back on header
+
+ } while (objType != 0);
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index e8bbdbdb3f..59ac6f39b6 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -28,7 +28,9 @@
#include "common/scummsys.h"
#include "common/serializer.h"
+#include "sci/engine/script.h"
#include "sci/engine/vm.h"
+#include "sci/engine/vm_types.h"
#include "sci/engine/segment.h"
namespace Sci {
@@ -42,8 +44,6 @@ namespace Sci {
error("%s, line, %d, %s", __FILE__, __LINE__, msg); \
}
-
-
/**
* Parameters for getScriptSegment().
*/
@@ -53,6 +53,7 @@ enum ScriptLoadType {
SCRIPT_GET_LOCK = 3 /**< Load, if neccessary, and lock */
};
+class Script;
class SegManager : public Common::Serializable {
friend class Console;
@@ -96,6 +97,11 @@ public:
void reconstructScripts(EngineState *s);
/**
+ * Reconstructs the stack. Used when restoring saved games
+ */
+ void reconstructStack(EngineState *s);
+
+ /**
* Determines the segment occupied by a certain script, if any.
* @param script_nr Number of the script to look up
* @return The script's segment ID, or 0 on failure
@@ -111,12 +117,30 @@ public:
*/
SegmentId getScriptSegment(int script_nr, ScriptLoadType load);
- // TODO: document this
- reg_t lookupScriptExport(int script_nr, int export_index) {
- SegmentId seg = getScriptSegment(script_nr, SCRIPT_GET_DONT_LOAD);
- return make_reg(seg, getScript(seg)->validateExportFunc(export_index));
- }
+ /**
+ * Makes sure that a script and its superclasses get loaded to the heap.
+ * If the script already has been loaded, only the number of lockers is
+ * increased. All scripts containing superclasses of this script are loaded
+ * recursively as well, unless 'recursive' is set to zero. The
+ * complementary function is "uninstantiateScript()" below.
+ * @param[in] script_nr The script number to load
+ * @return The script's segment ID or 0 if out of heap
+ */
+ int instantiateScript(int script_nr);
+ /**
+ * Decreases the numer of lockers of a script and unloads it if that number
+ * reaches zero.
+ * This function will recursively unload scripts containing its
+ * superclasses, if those aren't locked by other scripts as well.
+ * @param[in] script_nr The script number that is requestet to be unloaded
+ */
+ void uninstantiateScript(int script_nr);
+
+private:
+ void uninstantiateScriptSci0(int script_nr);
+
+public:
// TODO: document this
reg_t getClassAddress(int classnr, ScriptLoadType lock, reg_t caller);
@@ -137,28 +161,7 @@ public:
* @param seg ID of the script segment to check for
* @return A pointer to the Script object, or NULL
*/
- Script *getScriptIfLoaded(SegmentId seg);
-
-
- // 1b. Script Initialisation
-
- // The set of functions below are intended
- // to be used during script instantiation,
- // i.e. loading and linking.
-
- /**
- * Initializes a script's local variable block
- * All variables are initialized to zero.
- * @param seg Segment containing the script to initialize
- * @param nr Number of local variables to allocate
- */
- void scriptInitialiseLocalsZero(SegmentId seg, int nr);
-
- /**
- * Initializes a script's local variable block according to a prototype
- * @param location Location to initialize from
- */
- void scriptInitialiseLocals(reg_t location);
+ Script *getScriptIfLoaded(SegmentId seg) const;
// 2. Clones
@@ -188,16 +191,11 @@ public:
// 5. System Strings
/**
- * Allocates a system string table
- * See also sys_string_acquire();
- * @param[in] segid Segment ID of the stack
- * @returns The physical stack
+ * Initializes the system string table.
*/
- SystemStrings *allocateSysStrings(SegmentId *segid);
+ void initSysStrings();
- // 5. System Strings
-
// 6, 7. Lists and Nodes
/**
@@ -215,6 +213,14 @@ public:
Node *allocateNode(reg_t *addr);
/**
+ * Allocate and initialize a new list node.
+ * @param[in] value The value to set the node to
+ * @param[in] key The key to set
+ * @return Pointer to the newly initialized list node
+ */
+ reg_t newNode(reg_t value, reg_t key);
+
+ /**
* Resolves a list pointer to a list.
* @param addr The address to resolve
* @return The list referenced, or NULL on error
@@ -226,7 +232,7 @@ public:
* @param addr The address to resolve
* @return The list node referenced, or NULL on error
*/
- Node *lookupNode(reg_t addr);
+ Node *lookupNode(reg_t addr, bool stopOnDiscarded = true);
// 8. Hunk Memory
@@ -268,7 +274,7 @@ public:
* Deallocates a piece of dynamic memory
* @param[in] addr Offset of the dynmem chunk to free
*/
- int freeDynmem(reg_t addr);
+ bool freeDynmem(reg_t addr);
// Generic Operations on Segments and Addresses
@@ -381,39 +387,39 @@ public:
* @param type The type of the segment to find
* @return The segment number, or -1 if the segment wasn't found
*/
- SegmentId findSegmentByType(int type);
+ SegmentId findSegmentByType(int type) const;
// TODO: document this
- SegmentObj *getSegmentObj(SegmentId seg);
+ SegmentObj *getSegmentObj(SegmentId seg) const;
// TODO: document this
- SegmentType getSegmentType(SegmentId seg);
+ SegmentType getSegmentType(SegmentId seg) const;
// TODO: document this
- SegmentObj *getSegment(SegmentId seg, SegmentType type);
+ SegmentObj *getSegment(SegmentId seg, SegmentType type) const;
/**
* Retrieves an object from the specified location
* @param[in] offset Location (segment, offset) of the object
* @return The object in question, or NULL if there is none
*/
- Object *getObject(reg_t pos);
+ Object *getObject(reg_t pos) const;
/**
* Checks whether a heap address contains an object
* @parm obj The address to check
* @return True if it is an object, false otherwise
*/
- bool isObject(reg_t obj) { return getObject(obj) != NULL; }
+ bool isObject(reg_t obj) const { return getObject(obj) != NULL; }
// TODO: document this
- bool isHeapObject(reg_t pos);
+ bool isHeapObject(reg_t pos) const;
/**
* Determines the name of an object
* @param[in] pos Location (segment, offset) of the object
* @return A name for that object, or a string describing an error
- * that occured while looking it up. The string is stored
+ * that occurred while looking it up. The string is stored
* in a static buffer and need not be freed (neither may
* it be modified).
*/
@@ -432,12 +438,27 @@ public:
*/
reg_t findObjectByName(const Common::String &name, int index = -1);
- void scriptRelocateExportsSci11(SegmentId seg);
- void scriptInitialiseObjectsSci11(SegmentId seg);
+ uint32 classTableSize() const { return _classTable.size(); }
+ Class getClass(int index) const { return _classTable[index]; }
+ void setClassOffset(int index, reg_t offset) { _classTable[index].reg = offset; }
+ void resizeClassTable(uint32 size) { _classTable.resize(size); }
-public: // TODO: make private
- Common::Array<SegmentObj *> _heap;
- Common::Array<Class> _classtable; /**< Table of all classes */
+ /**
+ * Obtains the system strings segment ID
+ */
+ SegmentId getSysStringsSegment() { return _sysStringsSegId; }
+
+ /**
+ * Get a pointer to the system string with the specified index,
+ * or NULL if that index is invalid.
+ *
+ * This method is currently only used by kString().
+ */
+ SystemString *getSystemString(uint idx) const {
+ if (idx >= SYS_STRINGS_MAX)
+ return NULL;
+ return &_sysStrings->_strings[idx];
+ }
#ifdef ENABLE_SCI32
SciArray<reg_t> *allocateArray(reg_t *addr);
@@ -446,28 +467,35 @@ public: // TODO: make private
SciString *allocateString(reg_t *addr);
SciString *lookupString(reg_t addr);
void freeString(reg_t addr);
- SegmentId getStringSegmentId() { return String_seg_id; }
+ SegmentId getStringSegmentId() { return _stringSegId; }
#endif
+ const Common::Array<SegmentObj *> &getSegments() const { return _heap; }
+
private:
+ Common::Array<SegmentObj *> _heap;
+ Common::Array<Class> _classTable; /**< Table of all classes */
/** Map script ids to segment ids. */
Common::HashMap<int, SegmentId> _scriptSegMap;
ResourceManager *_resMan;
- SegmentId Clones_seg_id; ///< ID of the (a) clones segment
- SegmentId Lists_seg_id; ///< ID of the (a) list segment
- SegmentId Nodes_seg_id; ///< ID of the (a) node segment
- SegmentId Hunks_seg_id; ///< ID of the (a) hunk segment
+ SegmentId _clonesSegId; ///< ID of the (a) clones segment
+ SegmentId _listsSegId; ///< ID of the (a) list segment
+ SegmentId _nodesSegId; ///< ID of the (a) node segment
+ SegmentId _hunksSegId; ///< ID of the (a) hunk segment
+
+ /* System strings */
+ SegmentId _sysStringsSegId;
+ SystemStrings *_sysStrings;
#ifdef ENABLE_SCI32
- SegmentId Arrays_seg_id;
- SegmentId String_seg_id;
+ SegmentId _arraysSegId;
+ SegmentId _stringSegId;
#endif
private:
SegmentObj *allocSegment(SegmentObj *mem, SegmentId *segid);
- LocalVariables *allocLocalsSegment(Script *scr, int count);
int deallocate(SegmentId seg, bool recursive);
void createClassTable();
@@ -480,6 +508,9 @@ private:
* 'seg' is a valid segment
*/
bool check(SegmentId seg);
+
+public:
+ LocalVariables *allocLocalsSegment(Script *scr);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index ab1a68d165..b16dd5a5e5 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -27,6 +27,7 @@
#include "sci/sci.h"
#include "sci/engine/features.h"
+#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
#include "sci/engine/segment.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
@@ -85,333 +86,72 @@ SegmentObj *SegmentObj::createSegmentObj(SegmentType type) {
return mem;
}
-Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
- _nr = 0;
- _buf = NULL;
- _bufSize = 0;
- _scriptSize = 0;
- _heapSize = 0;
-
- _synonyms = NULL;
- _heapStart = NULL;
- _exportTable = NULL;
-
- _localsOffset = 0;
- _localsSegment = 0;
- _localsBlock = NULL;
-
- _relocated = false;
- _markedAsDeleted = 0;
-}
-
-Script::~Script() {
- freeScript();
-}
-
-void Script::freeScript() {
- free(_buf);
- _buf = NULL;
- _bufSize = 0;
-
- _objects.clear();
- _codeBlocks.clear();
-}
-
-bool Script::init(int script_nr, ResourceManager *resMan) {
- setScriptSize(script_nr, resMan);
-
- _buf = (byte *)malloc(_bufSize);
-
- if (!_buf) {
- freeScript();
- warning("Not enough memory space for script size");
- _bufSize = 0;
- return false;
- }
-
- _localsOffset = 0;
- _localsBlock = NULL;
-
- _codeBlocks.clear();
-
- _relocated = false;
- _markedAsDeleted = false;
-
- _nr = script_nr;
-
- if (getSciVersion() >= SCI_VERSION_1_1)
- _heapStart = _buf + _scriptSize;
- else
- _heapStart = _buf;
-
- return true;
-}
-
-void Script::setScriptSize(int script_nr, ResourceManager *resMan) {
- Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
- Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
-
- _scriptSize = script->size;
- _heapSize = 0; // Set later
-
- if (!script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) {
- error("SegManager::setScriptSize: failed to load %s", !script ? "script" : "heap");
- }
- if (oldScriptHeader) {
- _bufSize = script->size + READ_LE_UINT16(script->data) * 2;
- //locals_size = READ_LE_UINT16(script->data) * 2;
- } else if (getSciVersion() < SCI_VERSION_1_1) {
- _bufSize = script->size;
- } else {
- _bufSize = script->size + heap->size;
- _heapSize = heap->size;
-
- // Ensure that the start of the heap resource can be word-aligned.
- if (script->size & 2) {
- _bufSize++;
- _scriptSize++;
- }
-
- if (_bufSize > 65535) {
- error("Script and heap sizes combined exceed 64K."
- "This means a fundamental design bug was made in SCI\n"
- "regarding SCI1.1 games.\nPlease report this so it can be"
- "fixed in the next major version");
- return;
- }
+const char *SegmentObj::getSegmentTypeName(SegmentType type) {
+ switch (type) {
+ case SEG_TYPE_SCRIPT:
+ return "script";
+ break;
+ case SEG_TYPE_CLONES:
+ return "clones";
+ break;
+ case SEG_TYPE_LOCALS:
+ return "locals";
+ break;
+ case SEG_TYPE_SYS_STRINGS:
+ return "strings";
+ break;
+ case SEG_TYPE_STACK:
+ return "stack";
+ break;
+ case SEG_TYPE_HUNK:
+ return "hunk";
+ break;
+ case SEG_TYPE_LISTS:
+ return "lists";
+ break;
+ case SEG_TYPE_NODES:
+ return "nodes";
+ break;
+ case SEG_TYPE_DYNMEM:
+ return "dynmem";
+ break;
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_ARRAY:
+ return "array";
+ break;
+ case SEG_TYPE_STRING:
+ return "string";
+ break;
+#endif
+ default:
+ error("Unknown SegmentObj type %d", type);
+ break;
}
+ return NULL;
}
-Object *Script::allocateObject(uint16 offset) {
- return &_objects[offset];
-}
-
-Object *Script::getObject(uint16 offset) {
- if (_objects.contains(offset))
- return &_objects[offset];
- else
- return 0;
-}
-
-Object *Script::scriptObjInit(reg_t obj_pos) {
- Object *obj;
-
- if (getSciVersion() < SCI_VERSION_1_1)
- obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
-
- VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n");
-
- obj = allocateObject(obj_pos.offset);
-
- VERIFY(obj_pos.offset + SCRIPT_FUNCTAREAPTR_OFFSET < (int)_bufSize, "Function area pointer stored beyond end of script\n");
-
- obj->init(_buf, obj_pos);
-
- return obj;
-}
-
-void Script::scriptObjRemove(reg_t obj_pos) {
- if (getSciVersion() < SCI_VERSION_1_1)
- obj_pos.offset += 8;
-
- _objects.erase(obj_pos.toUint16());
-}
-
-int Script::relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location) {
+// This helper function is used by Script::relocateLocal and Object::relocate
+// Duplicate in segment.cpp and script.cpp
+static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
int rel = location - block_location;
if (rel < 0)
- return 0;
+ return false;
uint idx = rel >> 1;
if (idx >= block.size())
- return 0;
+ return false;
if (rel & 1) {
- warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
- return 0;
+ error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
+ return false;
}
block[idx].segment = segment; // Perform relocation
if (getSciVersion() >= SCI_VERSION_1_1)
- block[idx].offset += _scriptSize;
-
- return 1;
-}
-
-int Script::relocateLocal(SegmentId segment, int location) {
- if (_localsBlock)
- return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location);
- else
- return 0; // No hands, no cookies
-}
-
-int Script::relocateObject(Object &obj, SegmentId segment, int location) {
- return relocateBlock(obj._variables, obj.getPos().offset, segment, location);
-}
-
-void Script::scriptAddCodeBlock(reg_t location) {
- CodeBlock cb;
- cb.pos = location;
- cb.size = READ_SCI11ENDIAN_UINT16(_buf + location.offset - 2);
- _codeBlocks.push_back(cb);
-}
-
-void Script::scriptRelocate(reg_t block) {
- VERIFY(block.offset < (uint16)_bufSize && READ_SCI11ENDIAN_UINT16(_buf + block.offset) * 2 + block.offset < (uint16)_bufSize,
- "Relocation block outside of script\n");
-
- int count = READ_SCI11ENDIAN_UINT16(_buf + block.offset);
-
- for (int i = 0; i <= count; i++) {
- int pos = READ_SCI11ENDIAN_UINT16(_buf + block.offset + 2 + (i * 2));
- if (!pos)
- continue; // FIXME: A hack pending investigation
-
- if (!relocateLocal(block.segment, pos)) {
- bool done = false;
- uint k;
-
- ObjMap::iterator it;
- const ObjMap::iterator end = _objects.end();
- for (it = _objects.begin(); !done && it != end; ++it) {
- if (relocateObject(it->_value, block.segment, pos))
- done = true;
- }
-
- for (k = 0; !done && k < _codeBlocks.size(); k++) {
- if (pos >= _codeBlocks[k].pos.offset &&
- pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size)
- done = true;
- }
-
- if (!done) {
- printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block));
- printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count);
- if (_localsBlock)
- printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset);
- else
- printf("- No locals\n");
- for (it = _objects.begin(), k = 0; it != end; ++it, ++k)
- printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount());
- // SQ3 script 71 has broken relocation entries.
- printf("Trying to continue anyway...\n");
- }
- }
- }
-}
-
-void Script::heapRelocate(reg_t block) {
- VERIFY(block.offset < (uint16)_heapSize && READ_SCI11ENDIAN_UINT16(_heapStart + block.offset) * 2 + block.offset < (uint16)_bufSize,
- "Relocation block outside of script\n");
-
- if (_relocated)
- return;
- _relocated = true;
- int count = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset);
-
- for (int i = 0; i < count; i++) {
- int pos = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset + 2 + (i * 2)) + _scriptSize;
-
- if (!relocateLocal(block.segment, pos)) {
- bool done = false;
- uint k;
-
- ObjMap::iterator it;
- const ObjMap::iterator end = _objects.end();
- for (it = _objects.begin(); !done && it != end; ++it) {
- if (relocateObject(it->_value, block.segment, pos))
- done = true;
- }
-
- if (!done) {
- printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block));
- printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count);
- if (_localsBlock)
- printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset);
- else
- printf("- No locals\n");
- for (it = _objects.begin(), k = 0; it != end; ++it, ++k)
- printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount());
- error("Breakpoint in %s, line %d", __FILE__, __LINE__);
- }
- }
- }
-}
-
-void Script::incrementLockers() {
- _lockers++;
-}
-
-void Script::decrementLockers() {
- if (_lockers > 0)
- _lockers--;
-}
+ block[idx].offset += scriptSize;
-int Script::getLockers() const {
- return _lockers;
-}
-
-void Script::setLockers(int lockers) {
- _lockers = lockers;
-}
-
-void Script::setExportTableOffset(int offset) {
- if (offset) {
- _exportTable = (uint16 *)(_buf + offset + 2);
- _numExports = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable - 1));
- } else {
- _exportTable = NULL;
- _numExports = 0;
- }
-}
-
-uint16 Script::validateExportFunc(int pubfunct) {
- bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
-
- if (_numExports <= pubfunct) {
- warning("validateExportFunc(): pubfunct is invalid");
- return 0;
- }
-
- if (exportsAreWide)
- pubfunct *= 2;
- uint16 offset = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable + pubfunct));
- VERIFY(offset < _bufSize, "invalid export function pointer");
-
- return offset;
-}
-
-void Script::setSynonymsOffset(int offset) {
- _synonyms = _buf + offset;
-}
-
-byte *Script::getSynonyms() const {
- return _synonyms;
-}
-
-void Script::setSynonymsNr(int n) {
- _numSynonyms = n;
-}
-
-int Script::getSynonymsNr() const {
- return _numSynonyms;
-}
-
-// memory operations
-
-void Script::mcpyInOut(int dst, const void *src, size_t n) {
- if (_buf) {
- assert(dst + n <= _bufSize);
- memcpy(_buf + dst, src, n);
- }
-}
-
-int16 Script::getHeap(uint16 offset) const {
- assert(offset + 1 < (int)_bufSize);
- return READ_SCI11ENDIAN_UINT16(_buf + offset);
-// return (_buf[offset] | (_buf[offset+1]) << 8);
+ return true;
}
SegmentRef SegmentObj::dereference(reg_t pointer) {
@@ -420,23 +160,6 @@ SegmentRef SegmentObj::dereference(reg_t pointer) {
return SegmentRef();
}
-bool Script::isValidOffset(uint16 offset) const {
- return offset < _bufSize;
-}
-
-SegmentRef Script::dereference(reg_t pointer) {
- if (pointer.offset > _bufSize) {
- warning("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
- PRINT_REG(pointer), (uint)_bufSize);
- return SegmentRef();
- }
-
- SegmentRef ret;
- ret.isRaw = true;
- ret.maxSize = _bufSize - pointer.offset;
- ret.raw = _buf + pointer.offset;
- return ret;
-}
bool LocalVariables::isValidOffset(uint16 offset) const {
return offset < _locals.size() * 2;
@@ -455,7 +178,16 @@ SegmentRef LocalVariables::dereference(reg_t pointer) {
if (ret.maxSize > 0) {
ret.reg = &_locals[pointer.offset / 2];
} else {
- warning("LocalVariables::dereference: Offset at end or out of bounds %04x:%04x", PRINT_REG(pointer));
+ if ((g_sci->getEngineState()->currentRoomNumber() == 660 || g_sci->getEngineState()->currentRoomNumber() == 660)
+ && g_sci->getGameId() == GID_LAURABOW2) {
+ // Happens in two places during the intro of LB2CD, both from kMemory(peek):
+ // - room 160: Heap 160 has 83 local variables (0-82), and the game
+ // asks for variables at indices 83 - 90 too.
+ // - room 220: Heap 220 has 114 local variables (0-113), and the
+ // game asks for variables at indices 114-120 too.
+ } else {
+ error("LocalVariables::dereference: Offset at end or out of bounds %04x:%04x", PRINT_REG(pointer));
+ }
ret.reg = 0;
}
return ret;
@@ -502,84 +234,45 @@ SegmentRef SystemStrings::dereference(reg_t pointer) {
if (isValidOffset(pointer.offset))
ret.raw = (byte *)(_strings[pointer.offset]._value);
else {
- // This occurs in KQ5CD when interacting with certain objects
- warning("SystemStrings::dereference(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
- }
-
- return ret;
-}
-
-
-//-------------------- script --------------------
-reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) {
- addr.offset = 0;
- return addr;
-}
-
-void Script::freeAtAddress(SegManager *segMan, reg_t addr) {
- /*
- debugC(2, kDebugLevelGC, "[GC] Freeing script %04x:%04x", PRINT_REG(addr));
- if (_localsSegment)
- debugC(2, kDebugLevelGC, "[GC] Freeing locals %04x:0000", _localsSegment);
- */
-
- if (_markedAsDeleted)
- segMan->deallocateScript(_nr);
-}
-
-void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {
- (*note)(param, make_reg(segId, 0));
-}
-
-void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
- if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) {
- Object *obj = getObject(addr.offset);
- if (obj) {
- // Note all local variables, if we have a local variable environment
- if (_localsSegment)
- (*note)(param, make_reg(_localsSegment, 0));
-
- for (uint i = 0; i < obj->getVarCount(); i++)
- (*note)(param, obj->getVariable(i));
+ if (g_sci->getGameId() == GID_KQ5) {
+ // This occurs in KQ5CD when interacting with certain objects
} else {
- warning("Request for outgoing script-object reference at %04x:%04x failed", PRINT_REG(addr));
+ error("SystemStrings::dereference(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
}
- } else {
- /* warning("Unexpected request for outgoing script-object references at %04x:%04x", PRINT_REG(addr));*/
- /* Happens e.g. when we're looking into strings */
}
+
+ return ret;
}
//-------------------- clones --------------------
-void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
- Clone *clone;
-
+Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
// assert(addr.segment == _segId);
if (!isValidEntry(addr.offset)) {
error("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr));
}
- clone = &(_table[addr.offset]);
+ const Clone *clone = &(_table[addr.offset]);
// Emit all member variables (including references to the 'super' delegate)
for (uint i = 0; i < clone->getVarCount(); i++)
- (*note)(param, clone->getVariable(i));
+ tmp.push_back(clone->getVariable(i));
// Note that this also includes the 'base' object, which is part of the script and therefore also emits the locals.
- (*note)(param, clone->getPos());
+ tmp.push_back(clone->getPos());
//debugC(2, kDebugLevelGC, "[GC] Reporting clone-pos %04x:%04x", PRINT_REG(clone->pos));
+
+ return tmp;
}
void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
#ifdef GC_DEBUG
- Object *victim_obj;
+ // assert(addr.segment == _segId);
-// assert(addr.segment == _segId);
-
- victim_obj = &(_table[addr.offset]);
+ Object *victim_obj = &(_table[addr.offset]);
if (!(victim_obj->_flags & OBJECT_FLAG_FREED))
warning("[GC] Clone %04x:%04x not reachable and not freed (freeing now)", PRINT_REG(addr));
@@ -597,7 +290,7 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
//-------------------- locals --------------------
-reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) {
+reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const {
// Reference the owning script
SegmentId owner_seg = segMan->getScriptSegment(script_id);
@@ -606,25 +299,31 @@ reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) {
return make_reg(owner_seg, 0);
}
-void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+Common::Array<reg_t> LocalVariables::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
// assert(addr.segment == _segId);
for (uint i = 0; i < _locals.size(); i++)
- (*note)(param, _locals[i]);
+ tmp.push_back(_locals[i]);
+
+ return tmp;
}
//-------------------- stack --------------------
-reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) {
+reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const {
addr.offset = 0;
return addr;
}
-void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+Common::Array<reg_t> DataStack::listAllOutgoingReferences(reg_t object) const {
+ Common::Array<reg_t> tmp;
fprintf(stderr, "Emitting %d stack entries\n", _capacity);
for (int i = 0; i < _capacity; i++)
- (*note)(param, _entries[i]);
+ tmp.push_back(_entries[i]);
fprintf(stderr, "DONE");
+
+ return tmp;
}
@@ -633,18 +332,20 @@ void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+Common::Array<reg_t> ListTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
if (!isValidEntry(addr.offset)) {
- warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
- return;
+ error("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- List *list = &(_table[addr.offset]);
+ const List *list = &(_table[addr.offset]);
- note(param, list->first);
- note(param, list->last);
+ tmp.push_back(list->first);
+ tmp.push_back(list->last);
// We could probably get away with just one of them, but
// let's be conservative here.
+
+ return tmp;
}
@@ -653,19 +354,21 @@ void NodeTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
if (!isValidEntry(addr.offset)) {
- warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
- return;
+ error("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- Node *node = &(_table[addr.offset]);
+ const Node *node = &(_table[addr.offset]);
// We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us
// to walk around from any given node
- note(param, node->pred);
- note(param, node->succ);
- note(param, node->key);
- note(param, node->value);
+ tmp.push_back(node->pred);
+ tmp.push_back(node->succ);
+ tmp.push_back(node->key);
+ tmp.push_back(node->value);
+
+ return tmp;
}
@@ -673,22 +376,45 @@ void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback
//-------------------- object ----------------------------
-Object *Object::getClass(SegManager *segMan) {
+void Object::init(byte *buf, reg_t obj_pos, bool initVariables) {
+ byte *data = buf + obj_pos.offset;
+ _baseObj = data;
+ _pos = obj_pos;
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ _variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter));
+ _baseVars = (const uint16 *)(_baseObj + _variables.size() * 2);
+ _baseMethod = (const uint16 *)(data + READ_LE_UINT16(data + kOffsetFunctionArea));
+ _methodCount = READ_LE_UINT16(_baseMethod - 1);
+ } else {
+ _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2));
+ _baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4));
+ _baseMethod = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6));
+ _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod);
+ }
+
+ if (initVariables) {
+ for (uint i = 0; i < _variables.size(); i++)
+ _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2)));
+ }
+}
+
+const Object *Object::getClass(SegManager *segMan) const {
return isClass() ? this : segMan->getObject(getSuperClassSelector());
}
-int Object::locateVarSelector(SegManager *segMan, Selector slc) {
- byte *buf;
+int Object::locateVarSelector(SegManager *segMan, Selector slc) const {
+ const byte *buf;
uint varnum;
if (getSciVersion() < SCI_VERSION_1_1) {
varnum = getVarCount();
- int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET;
+ int selector_name_offset = varnum * 2 + kOffsetSelectorSegment;
buf = _baseObj + selector_name_offset;
} else {
- Object *obj = getClass(segMan);
+ const Object *obj = getClass(segMan);
varnum = obj->getVariable(1).toUint16();
- buf = (byte *)obj->_baseVars;
+ buf = (const byte *)obj->_baseVars;
}
for (uint i = 0; i < varnum; i++)
@@ -698,15 +424,74 @@ int Object::locateVarSelector(SegManager *segMan, Selector slc) {
return -1; // Failed
}
+bool Object::relocate(SegmentId segment, int location, size_t scriptSize) {
+ return relocateBlock(_variables, getPos().offset, segment, location, scriptSize);
+}
+
+int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const {
+ int selectors = getVarCount();
+
+ if (propertyOffset < 0 || (propertyOffset >> 1) >= selectors) {
+ error("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])",
+ propertyOffset, propertyOffset >> 1, selectors - 1);
+ return -1;
+ }
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2;
+ return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset);
+ } else {
+ const Object *obj = this;
+ if (!isClass())
+ obj = segMan->getObject(getSuperClassSelector());
+
+ return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset);
+ }
+}
+
+void Object::initSpecies(SegManager *segMan, reg_t addr) {
+ uint16 speciesOffset = getSpeciesSelector().offset;
+
+ if (speciesOffset == 0xffff) // -1
+ setSpeciesSelector(NULL_REG); // no species
+ else
+ setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr));
+}
+
+void Object::initSuperClass(SegManager *segMan, reg_t addr) {
+ uint16 superClassOffset = getSuperClassSelector().offset;
+
+ if (superClassOffset == 0xffff) // -1
+ setSuperClassSelector(NULL_REG); // no superclass
+ else
+ setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr));
+}
+
+bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) {
+ const Object *baseObj = segMan->getObject(getSpeciesSelector());
+
+ if (baseObj) {
+ _variables.resize(baseObj->getVarCount());
+ // Copy base from species class, as we need its selector IDs
+ _baseObj = baseObj->_baseObj;
+ if (doInitSuperClass)
+ initSuperClass(segMan, addr);
+ return true;
+ }
+
+ return false;
+}
+
//-------------------- dynamic memory --------------------
-reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) {
+reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const {
addr.offset = 0;
return addr;
}
-void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {
- (*note)(param, make_reg(segId, 0));
+Common::Array<reg_t> DynMem::listAllDeallocatable(SegmentId segId) const {
+ const reg_t r = make_reg(segId, 0);
+ return Common::Array<reg_t>(&r, 1);
}
#ifdef ENABLE_SCI32
@@ -724,22 +509,24 @@ void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+Common::Array<reg_t> ArrayTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
if (!isValidEntry(addr.offset)) {
- warning("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
- return;
+ error("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- SciArray<reg_t> *array = &(_table[addr.offset]);
+ const SciArray<reg_t> *array = &(_table[addr.offset]);
for (uint32 i = 0; i < array->getSize(); i++) {
reg_t value = array->getValue(i);
if (value.segment != 0)
- note(param, value);
+ tmp.push_back(value);
}
+
+ return tmp;
}
-Common::String SciString::toString() {
+Common::String SciString::toString() const {
if (_type != 3)
error("SciString::toString(): Array is not a string");
@@ -750,7 +537,7 @@ Common::String SciString::toString() {
return string;
}
-void SciString::fromString(Common::String string) {
+void SciString::fromString(const Common::String &string) {
if (_type != 3)
error("SciString::fromString(): Array is not a string");
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 1089ada475..c8cb4cd203 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -67,7 +67,7 @@ enum SegmentType {
SEG_TYPE_NODES = 7,
SEG_TYPE_HUNK = 8,
SEG_TYPE_DYNMEM = 9,
- SEG_TYPE_STRING_FRAG = 10, // obsolete, we keep it to be able to load old saves
+ // 10 used to be string fragments, now obsolete
#ifdef ENABLE_SCI32
SEG_TYPE_ARRAY = 11,
@@ -80,10 +80,9 @@ enum SegmentType {
struct SegmentObj : public Common::Serializable {
SegmentType _type;
- typedef void (*NoteCallback)(void *param, reg_t addr); // FIXME: Bad choice of name
-
public:
static SegmentObj *createSegmentObj(SegmentType type);
+ static const char *getSegmentTypeName(SegmentType type);
public:
SegmentObj(SegmentType type) : _type(type) {}
@@ -106,40 +105,44 @@ public:
/**
* Finds the canonic address associated with sub_reg.
+ * Used by the garbage collector.
*
* For each valid address a, there exists a canonic address c(a) such that c(a) = c(c(a)).
* This address "governs" a in the sense that deallocating c(a) will deallocate a.
*
* @param sub_addr base address whose canonic address is to be found
*/
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) { return sub_addr; }
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const { return sub_addr; }
/**
* Deallocates all memory associated with the specified address.
+ * Used by the garbage collector.
* @param sub_addr address (within the given segment) to deallocate
*/
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {}
/**
- * Iterates over and reports all addresses within the current segment.
- * @param note Invoked for each address on which free_at_address() makes sense
- * @param param parameter passed to 'note'
+ * Iterates over and reports all addresses within the segment.
+ * Used by the garbage collector.
+ * @return a list of addresses within the segment
*/
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {}
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const {
+ return Common::Array<reg_t>();
+ }
/**
* Iterates over all references reachable from the specified object.
- * @param object object (within the current segment) to analyse
- * @param param parameter passed to 'note'
- * @param note Invoked for each outgoing reference within the object
- * Note: This function may also choose to report numbers (segment 0) as adresses
+ * Used by the garbage collector.
+ * @param object object (within the current segment) to analyse
+ * @return a list of outgoing references within the object
+ *
+ * @note This function may also choose to report numbers (segment 0) as adresses
*/
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) {}
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const {
+ return Common::Array<reg_t>();
+ }
};
-
-struct IntMapper;
-
enum {
SYS_STRINGS_MAX = 4,
@@ -194,8 +197,8 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -205,6 +208,22 @@ enum {
OBJECT_FLAG_FREED = (1 << 0)
};
+enum infoSelectorFlags {
+ kInfoFlagClone = 0x0001,
+ kInfoFlagClass = 0x8000
+};
+
+enum ObjectOffsets {
+ kOffsetLocalVariables = -6,
+ kOffsetFunctionArea = -4,
+ kOffsetSelectorCounter = -2,
+ kOffsetSelectorSegment = 0,
+ kOffsetInfoSelectorSci0 = 4,
+ kOffsetNamePointerSci0 = 6,
+ kOffsetInfoSelectorSci11 = 14,
+ kOffsetNamePointerSci11 = 16
+};
+
class Object {
public:
Object() {
@@ -214,31 +233,34 @@ public:
~Object() { }
- reg_t getSpeciesSelector() { return _variables[_offset]; }
+ reg_t getSpeciesSelector() const { return _variables[_offset]; }
void setSpeciesSelector(reg_t value) { _variables[_offset] = value; }
- reg_t getSuperClassSelector() { return _variables[_offset + 1]; }
+ reg_t getSuperClassSelector() const { return _variables[_offset + 1]; }
void setSuperClassSelector(reg_t value) { _variables[_offset + 1] = value; }
- reg_t getInfoSelector() { return _variables[_offset + 2]; }
- void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; }
+ reg_t getInfoSelector() const { return _variables[_offset + 2]; }
+ void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; }
+
+ reg_t getNameSelector() const { return _variables[_offset + 3]; }
+ void setNameSelector(reg_t value) { _variables[_offset + 3] = value; }
- reg_t getNameSelector() { return _variables[_offset + 3]; }
- void setNameSelector(reg_t value) { _variables[_offset + 3] = value; }
+ reg_t getPropDictSelector() const { return _variables[2]; }
+ void setPropDictSelector(reg_t value) { _variables[2] = value; }
- reg_t getClassScriptSelector() { return _variables[4]; }
+ reg_t getClassScriptSelector() const { return _variables[4]; }
void setClassScriptSelector(reg_t value) { _variables[4] = value; }
- Selector getVarSelector(uint16 i) { return READ_SCI11ENDIAN_UINT16(_baseVars + i); }
+ Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); }
- reg_t getFunction(uint16 i) {
+ reg_t getFunction(uint16 i) const {
uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2;
- return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset)));
+ return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16(_baseMethod + offset));
}
- Selector getFuncSelector(uint16 i) {
+ Selector getFuncSelector(uint16 i) const {
uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? i : i * 2 + 1;
- return READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset));
+ return READ_SCI11ENDIAN_UINT16(_baseMethod + offset);
}
/**
@@ -247,7 +269,7 @@ public:
* superclasses, i.e. failure may be returned even if one of the
* superclasses defines the funcselector
*/
- int funcSelectorPosition(Selector sel) {
+ int funcSelectorPosition(Selector sel) const {
for (uint i = 0; i < _methodCount; i++)
if (getFuncSelector(i) == sel)
return i;
@@ -256,260 +278,60 @@ public:
}
/**
- * Determines if the object explicitly defines slc as a varselector
- * Returns -1 if not found
+ * Determines if the object explicitly defines slc as a varselector.
+ * Returns -1 if not found.
*/
- int locateVarSelector(SegManager *segMan, Selector slc);
+ int locateVarSelector(SegManager *segMan, Selector slc) const;
- bool isClass() { return (getInfoSelector().offset & SCRIPT_INFO_CLASS); }
- Object *getClass(SegManager *segMan);
+ bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); }
+ const Object *getClass(SegManager *segMan) const;
- void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
- bool isFreed() { return _flags & OBJECT_FLAG_FREED; }
-
- void setVarCount(uint size) { _variables.resize(size); }
- uint getVarCount() { return _variables.size(); }
+ void markAsClone() { setInfoSelector(make_reg(0, kInfoFlagClone)); }
+ bool isClone() const { return (getInfoSelector().offset & kInfoFlagClone); }
- void init(byte *buf, reg_t obj_pos) {
- byte *data = (byte *)(buf + obj_pos.offset);
- _baseObj = data;
- _pos = obj_pos;
+ void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
+ bool isFreed() const { return _flags & OBJECT_FLAG_FREED; }
- if (getSciVersion() < SCI_VERSION_1_1) {
- _variables.resize(READ_LE_UINT16(data + SCRIPT_SELECTORCTR_OFFSET));
- _baseVars = (uint16 *)(_baseObj + _variables.size() * 2);
- _baseMethod = (uint16 *)(data + READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET));
- _methodCount = READ_LE_UINT16(_baseMethod - 1);
- } else {
- _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2));
- _baseVars = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4));
- _baseMethod = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6));
- _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod);
- }
+ uint getVarCount() const { return _variables.size(); }
- for (uint i = 0; i < _variables.size(); i++)
- _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2)));
- }
+ void init(byte *buf, reg_t obj_pos, bool initVariables = true);
- reg_t getVariable(uint var) { return _variables[var]; }
+ reg_t getVariable(uint var) const { return _variables[var]; }
+ reg_t &getVariableRef(uint var) { return _variables[var]; }
- uint16 getMethodCount() { return _methodCount; }
- reg_t getPos() { return _pos; }
+ uint16 getMethodCount() const { return _methodCount; }
+ reg_t getPos() const { return _pos; }
void saveLoadWithSerializer(Common::Serializer &ser);
- void cloneFromObject(Object *obj) {
+ void cloneFromObject(const Object *obj) {
_baseObj = obj ? obj->_baseObj : NULL;
_baseMethod = obj ? obj->_baseMethod : NULL;
_baseVars = obj ? obj->_baseVars : NULL;
}
- // TODO: make private
- Common::Array<reg_t> _variables;
- byte *_baseObj; /**< base + object offset within base */
- uint16 *_baseVars; /**< Pointer to the varselector area for this object */
- uint16 *_baseMethod; /**< Pointer to the method selector area for this object */
-
-private:
- uint16 _methodCount;
- int _flags;
- uint16 _offset;
- reg_t _pos; /**< Object offset within its script; for clones, this is their base */
-};
+ bool relocate(SegmentId segment, int location, size_t scriptSize);
-struct CodeBlock {
- reg_t pos;
- int size;
-};
+ int propertyOffsetToId(SegManager *segMan, int propertyOffset) const;
-typedef Common::HashMap<uint16, Object> ObjMap;
-
-class Script : public SegmentObj {
-public:
- int _nr; /**< Script number */
- byte *_buf; /**< Static data buffer, or NULL if not used */
- size_t _bufSize;
- size_t _scriptSize;
- size_t _heapSize;
-
- byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
-
- uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
- int _numExports; /**< Number of entries in the exports table */
-
- byte *_synonyms; /**< Synonyms block or 0 if not present*/
- int _numSynonyms; /**< Number of entries in the synonyms block */
-
-protected:
- int _lockers; /**< Number of classes and objects that require this script */
-
-public:
- /**
- * Table for objects, contains property variables.
- * Indexed by the TODO offset.
- */
- ObjMap _objects;
-
- int _localsOffset;
- SegmentId _localsSegment; /**< The local variable segment */
- LocalVariables *_localsBlock;
-
- Common::Array<CodeBlock> _codeBlocks;
- bool _relocated;
- bool _markedAsDeleted;
+ void initSpecies(SegManager *segMan, reg_t addr);
+ void initSuperClass(SegManager *segMan, reg_t addr);
+ bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true);
+ // TODO: make private
+ // Only SegManager::reconstructScripts() is left needing direct access to these
public:
- Script();
- ~Script();
-
- void freeScript();
- bool init(int script_nr, ResourceManager *resMan);
-
- virtual bool isValidOffset(uint16 offset) const;
- virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
- virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
-
- virtual void saveLoadWithSerializer(Common::Serializer &ser);
-
- Object *allocateObject(uint16 offset);
- Object *getObject(uint16 offset);
-
- /**
- * Informs the segment manager that a code block must be relocated
- * @param location Start of block to relocate
- */
- void scriptAddCodeBlock(reg_t location);
-
- /**
- * Initializes an object within the segment manager
- * @param obj_pos Location (segment, offset) of the object. It must
- * point to the beginning of the script/class block
- * (as opposed to what the VM considers to be the
- * object location)
- * @returns A newly created Object describing the object,
- * stored within the relevant script
- */
- Object *scriptObjInit(reg_t obj_pos);
-
- /**
- * Removes a script object
- * @param obj_pos Location (segment, offset) of the object.
- */
- void scriptObjRemove(reg_t obj_pos);
-
- /**
- * Processes a relocation block witin a script
- * This function is idempotent, but it must only be called after all
- * objects have been instantiated, or a run-time error will occur.
- * @param obj_pos Location (segment, offset) of the block
- * @return Location of the relocation block
- */
- void scriptRelocate(reg_t block);
-
- void heapRelocate(reg_t block);
+ const byte *_baseObj; /**< base + object offset within base */
private:
- int relocateLocal(SegmentId segment, int location);
- int relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location);
- int relocateObject(Object &obj, SegmentId segment, int location);
-
-public:
- // script lock operations
-
- /** Increments the number of lockers of this script by one. */
- void incrementLockers();
-
- /** Decrements the number of lockers of this script by one. */
- void decrementLockers();
-
- /**
- * Retrieves the number of locks held on this script.
- * @return the number of locks held on the previously identified script
- */
- int getLockers() const;
+ const uint16 *_baseVars; /**< Pointer to the varselector area for this object */
+ const uint16 *_baseMethod; /**< Pointer to the method selector area for this object */
- /** Sets the number of locks held on this script. */
- void setLockers(int lockers);
-
- /**
- * Retrieves a pointer to the synonyms associated with this script
- * @return pointer to the synonyms, in non-parsed format.
- */
- byte *getSynonyms() const;
-
- /**
- * Retrieves the number of synonyms associated with this script.
- * @return the number of synonyms associated with this script
- */
- int getSynonymsNr() const;
-
- /**
- * Sets the script-relative offset of the exports table.
- * @param offset script-relative exports table offset
- */
- void setExportTableOffset(int offset);
-
- /**
- * Validate whether the specified public function is exported by
- * the script in the specified segment.
- * @param pubfunct Index of the function to validate
- * @return NULL if the public function is invalid, its
- * offset into the script's segment otherwise
- */
- uint16 validateExportFunc(int pubfunct);
-
- /**
- * Sets the script-relative offset of the synonyms associated with this script.
- * @param offset script-relative offset of the synonyms block
- */
- void setSynonymsOffset(int offset);
-
- /**
- * Sets the number of synonyms associated with this script,
- * @param nr number of synonyms, as to be stored within the script
- */
- void setSynonymsNr(int nr);
-
-
- /**
- * Marks the script as deleted.
- * This will not actually delete the script. If references remain present on the
- * heap or the stack, the script will stay in memory in a quasi-deleted state until
- * either unreachable (resulting in its eventual deletion) or reloaded (resulting
- * in its data being updated).
- */
- void markDeleted() {
- _markedAsDeleted = true;
- }
-
- /**
- * Determines whether the script is marked as being deleted.
- */
- bool isMarkedAsDeleted() const {
- return _markedAsDeleted;
- }
-
- /**
- * Copies a byte string into a script's heap representation.
- * @param dst script-relative offset of the destination area
- * @param src pointer to the data source location
- * @param n number of bytes to copy
- */
- void mcpyInOut(int dst, const void *src, size_t n);
-
-
- /**
- * Retrieves a 16 bit value from within a script's heap representation.
- * @param offset offset to read from
- * @return the value read from the specified location
- */
- int16 getHeap(uint16 offset) const;
-
-private:
- void setScriptSize(int script_nr, ResourceManager *resMan);
+ Common::Array<reg_t> _variables;
+ uint16 _methodCount;
+ int _flags;
+ uint16 _offset;
+ reg_t _pos; /**< Object offset within its script; for clones, this is their base */
};
/** Data stack */
@@ -529,8 +351,8 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -618,10 +440,12 @@ public:
entries_used--;
}
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const {
+ Common::Array<reg_t> tmp;
for (uint i = 0; i < _table.size(); i++)
if (isValidEntry(i))
- (*note)(param, make_reg(segId, i));
+ tmp.push_back(make_reg(segId, i));
+ return tmp;
}
};
@@ -631,7 +455,7 @@ struct CloneTable : public Table<Clone> {
CloneTable() : Table<Clone>(SEG_TYPE_CLONES) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -642,7 +466,7 @@ struct NodeTable : public Table<Node> {
NodeTable() : Table<Node>(SEG_TYPE_NODES) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -653,7 +477,7 @@ struct ListTable : public Table<List> {
ListTable() : Table<List>(SEG_TYPE_LISTS) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -666,7 +490,10 @@ struct HunkTable : public Table<Hunk> {
virtual void freeEntry(int idx) {
Table<Hunk>::freeEntry(idx);
+ if (!_table[idx].mem)
+ warning("Attempt to free an already freed hunk");
free(_table[idx].mem);
+ _table[idx].mem = 0;
}
virtual void saveLoadWithSerializer(Common::Serializer &ser);
@@ -688,8 +515,8 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -782,7 +609,7 @@ public:
_size = _actualSize = size;
}
- T getValue(uint16 index) {
+ T getValue(uint16 index) const {
if (index >= _size)
error("SciArray::getValue(): %d is out of bounds (%d)", index, _size);
@@ -796,9 +623,10 @@ public:
_data[index] = value;
}
- byte getType() { return _type; }
- uint32 getSize() { return _size; }
+ byte getType() const { return _type; }
+ uint32 getSize() const { return _size; }
T *getRawData() { return _data; }
+ const T *getRawData() const { return _data; }
protected:
int8 _type;
@@ -814,15 +642,15 @@ public:
// We overload destroy to ensure the string type is 3 after destroying
void destroy() { SciArray<char>::destroy(); _type = 3; }
- Common::String toString();
- void fromString(Common::String string);
+ Common::String toString() const;
+ void fromString(const Common::String &string);
};
struct ArrayTable : public Table<SciArray<reg_t> > {
ArrayTable() : Table<SciArray<reg_t> >(SEG_TYPE_ARRAY) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
void saveLoadWithSerializer(Common::Serializer &ser);
SegmentRef dereference(reg_t pointer);
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index e226c4b574..f99a41e088 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -24,6 +24,7 @@
*/
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -103,6 +104,8 @@ void Kernel::mapSelectors() {
FIND_SELECTOR2(b_incr, "b-incr");
FIND_SELECTOR(xStep);
FIND_SELECTOR(yStep);
+ FIND_SELECTOR(xLast);
+ FIND_SELECTOR(yLast);
FIND_SELECTOR(moveSpeed);
FIND_SELECTOR(canBeHere); // cantBeHere
FIND_SELECTOR(heading);
@@ -153,11 +156,15 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(subtitleLang);
FIND_SELECTOR(parseLang);
FIND_SELECTOR(overlay);
- FIND_SELECTOR(setCursor);
FIND_SELECTOR(topString);
FIND_SELECTOR(scaleSignal);
FIND_SELECTOR(scaleX);
FIND_SELECTOR(scaleY);
+ FIND_SELECTOR(maxScale);
+ FIND_SELECTOR(vanishingX);
+ FIND_SELECTOR(vanishingY);
+ FIND_SELECTOR(iconIndex);
+ FIND_SELECTOR(port);
#ifdef ENABLE_SCI32
FIND_SELECTOR(data);
@@ -172,58 +179,60 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(dimmed);
FIND_SELECTOR(fore);
FIND_SELECTOR(back);
+ FIND_SELECTOR(fixPriority);
+ FIND_SELECTOR(mirrored);
+ FIND_SELECTOR(useInsetRect);
+ FIND_SELECTOR(inTop);
+ FIND_SELECTOR(inLeft);
+ FIND_SELECTOR(inBottom);
+ FIND_SELECTOR(inRight);
#endif
}
-reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id) {
+reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) {
ObjVarRef address;
- if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable)
+ if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable)
return NULL_REG;
else
return *address.getPointer(segMan);
}
-void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value) {
+void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value) {
ObjVarRef address;
- if ((selector_id < 0) || (selector_id > (int)g_sci->getKernel()->getSelectorNamesSize())) {
- warning("Attempt to write to invalid selector %d of"
- " object at %04x:%04x.", selector_id, PRINT_REG(object));
+ if ((selectorId < 0) || (selectorId > (int)g_sci->getKernel()->getSelectorNamesSize())) {
+ error("Attempt to write to invalid selector %d of"
+ " object at %04x:%04x.", selectorId, PRINT_REG(object));
return;
}
- if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable)
- warning("Selector '%s' of object at %04x:%04x could not be"
- " written to", g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object));
+ if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable)
+ error("Selector '%s' of object at %04x:%04x could not be"
+ " written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
else
*address.getPointer(segMan) = value;
}
-int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
+void invokeSelector(EngineState *s, reg_t object, int selectorId,
int k_argc, StackPtr k_argp, int argc, const reg_t *argv) {
int i;
int framesize = 2 + 1 * argc;
- reg_t address;
int slc_type;
StackPtr stackframe = k_argp + k_argc;
- stackframe[0] = make_reg(0, selector_id); // The selector we want to call
+ stackframe[0] = make_reg(0, selectorId); // The selector we want to call
stackframe[1] = make_reg(0, argc); // Argument count
- slc_type = lookup_selector(s->_segMan, object, selector_id, NULL, &address);
+ slc_type = lookupSelector(s->_segMan, object, selectorId, NULL, NULL);
if (slc_type == kSelectorNone) {
- warning("Selector '%s' of object at %04x:%04x could not be invoked",
- g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object));
- if (noinvalid == kStopOnInvalidSelector)
- error("[Kernel] Not recoverable: VM was halted");
- return 1;
+ error("Selector '%s' of object at %04x:%04x could not be invoked",
+ g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
}
if (slc_type == kSelectorVariable) {
- warning("Attempting to invoke variable selector %s of object %04x:%04x",
- g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object));
- return 0;
+ error("Attempting to invoke variable selector %s of object %04x:%04x",
+ g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
}
for (i = 0; i < argc; i++)
@@ -237,43 +246,25 @@ int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, Selector
xstack->sp += argc + 2;
xstack->fp += argc + 2;
- run_vm(s, false); // Start a new vm
-
- return 0;
-}
-
-int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
- int k_argc, StackPtr k_argp, int argc, ...) {
- va_list argp;
- reg_t *args = new reg_t[argc];
-
- va_start(argp, argc);
- for (int i = 0; i < argc; i++)
- args[i] = va_arg(argp, reg_t);
- va_end(argp);
-
- int retval = invoke_selector_argv(s, object, selector_id, noinvalid, k_argc, k_argp, argc, args);
-
- delete[] args;
- return retval;
+ run_vm(s); // Start a new vm
}
-SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector selector_id, ObjVarRef *varp, reg_t *fptr) {
- Object *obj = segMan->getObject(obj_location);
+SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) {
+ const Object *obj = segMan->getObject(obj_location);
int index;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
// Early SCI versions used the LSB in the selector ID as a read/write
// toggle, meaning that we must remove it for selector lookup.
if (oldScriptHeader)
- selector_id &= ~1;
+ selectorId &= ~1;
if (!obj) {
- error("lookup_selector(): Attempt to send to non-object or invalid script. Address was %04x:%04x",
+ error("lookupSelector(): Attempt to send to non-object or invalid script. Address was %04x:%04x",
PRINT_REG(obj_location));
}
- index = obj->locateVarSelector(segMan, selector_id);
+ index = obj->locateVarSelector(segMan, selectorId);
if (index >= 0) {
// Found it as a variable
@@ -285,7 +276,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se
} else {
// Check if it's a method, with recursive lookup in superclasses
while (obj) {
- index = obj->funcSelectorPosition(selector_id);
+ index = obj->funcSelectorPosition(selectorId);
if (index >= 0) {
if (fptr)
*fptr = obj->getFunction(index);
@@ -300,7 +291,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se
}
-// return _lookup_selector_function(segMan, obj, selector_id, fptr);
+// return _lookupSelector_function(segMan, obj, selectorId, fptr);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 70eeb34d93..00e795c1b9 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -30,24 +30,133 @@
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/engine/vm.h"
-#include "sci/engine/kernel.h" // for Kernel::_selectorCache
namespace Sci {
-
-/******************** Selector functionality ********************/
-
-enum SelectorInvocation {
- kStopOnInvalidSelector = 0,
- kContinueOnInvalidSelector = 1
+/** Contains selector IDs for a few selected selectors */
+struct SelectorCache {
+ SelectorCache() {
+ memset(this, 0, sizeof(*this));
+ }
+
+ // Statically defined selectors, (almost the) same in all SCI versions
+ Selector y;
+ Selector x;
+ Selector view, loop, cel; ///< Description of a specific image
+ Selector underBits; ///< Used by the graphics subroutines to store backupped BG pic data
+ Selector nsTop, nsLeft, nsBottom, nsRight; ///< View boundaries ('now seen')
+ Selector lsTop, lsLeft, lsBottom, lsRight; ///< Used by Animate() subfunctions and scroll list controls
+ Selector signal; ///< Used by Animate() to control a view's behaviour
+ Selector illegalBits; ///< Used by CanBeHere
+ Selector brTop, brLeft, brBottom, brRight; ///< Bounding Rectangle
+ // name, key, time
+ Selector text; ///< Used by controls
+ Selector elements; ///< Used by SetSynonyms()
+ // color, back
+ Selector mode; ///< Used by text controls (-> DrawControl())
+ // style
+ Selector state, font, type;///< Used by controls
+ // window
+ Selector cursor, max; ///< Used by EditControl
+ // mark, who
+ Selector message; ///< Used by GetEvent
+ // edit
+ Selector play; ///< Play function (first function to be called)
+ Selector number;
+ Selector handle; ///< Replaced by nodePtr in SCI1+
+ Selector nodePtr; ///< Replaces handle in SCI1+
+ Selector client; ///< The object that wants to be moved
+ Selector dx, dy; ///< Deltas
+ Selector b_movCnt, b_i1, b_i2, b_di, b_xAxis, b_incr; ///< Various Bresenham vars
+ Selector xStep, yStep; ///< BR adjustments
+ Selector xLast, yLast; ///< BR last position of client
+ Selector moveSpeed; ///< Used for DoBresen
+ Selector canBeHere; ///< Funcselector: Checks for movement validity in SCI0
+ Selector heading, mover; ///< Used in DoAvoider
+ Selector doit; ///< Called (!) by the Animate() system call
+ Selector isBlocked, looper; ///< Used in DoAvoider
+ Selector priority;
+ Selector modifiers; ///< Used by GetEvent
+ Selector replay; ///< Replay function
+ // setPri, at, next, done, width
+ Selector wordFail, syntaxFail; ///< Used by Parse()
+ // semanticFail, pragmaFail
+ // said
+ Selector claimed; ///< Used generally by the event mechanism
+ // value, save, restore, title, button, icon, draw
+ Selector delete_; ///< Called by Animate() to dispose a view object
+ Selector z;
+
+ // SCI1+ static selectors
+ Selector parseLang;
+ Selector printLang; ///< Used for i18n
+ Selector subtitleLang;
+ Selector size;
+ Selector points; ///< Used by AvoidPath()
+ Selector palette;
+ Selector dataInc;
+ // handle (in SCI1)
+ Selector min; ///< SMPTE time format
+ Selector sec;
+ Selector frame;
+ Selector vol;
+ Selector pri;
+ // perform
+ Selector moveDone; ///< used for DoBresen
+
+ // SCI1 selectors which have been moved a bit in SCI1.1, but otherwise static
+ Selector cantBeHere; ///< Checks for movement avoidance in SCI1+. Replaces canBeHere
+ Selector topString; ///< SCI1 scroll lists use this instead of lsTop
+ Selector flags;
+
+ // SCI1+ audio sync related selectors, not static. They're used for lip syncing in
+ // CD talkie games
+ Selector syncCue; ///< Used by DoSync()
+ Selector syncTime;
+
+ // SCI1.1 specific selectors
+ Selector scaleSignal; //< Used by kAnimate() for cel scaling (SCI1.1+)
+ Selector scaleX, scaleY; ///< SCI1.1 view scaling
+ Selector maxScale; ///< SCI1.1 view scaling, limit for cel, when using global scaling
+ Selector vanishingX; ///< SCI1.1 view scaling, used by global scaling
+ Selector vanishingY; ///< SCI1.1 view scaling, used by global scaling
+
+ // Used for auto detection purposes
+ Selector overlay; ///< Used to determine if a game is using old gfx functions or not
+
+ // SCI1.1 Mac icon bar selectors
+ Selector iconIndex; ///< Used to index icon bar objects
+
+ Selector port; // used by a hoyle 4 workaround
+
+#ifdef ENABLE_SCI32
+ Selector data; // Used by Array()/String()
+ Selector picture; // Used to hold the picture ID for SCI32 pictures
+
+ Selector plane;
+ Selector top;
+ Selector left;
+ Selector bottom;
+ Selector right;
+ Selector resX;
+ Selector resY;
+
+ Selector fore;
+ Selector back;
+ Selector dimmed;
+
+ Selector fixPriority;
+ Selector mirrored;
+
+ Selector useInsetRect;
+ Selector inTop, inLeft, inBottom, inRight;
+#endif
};
-
/**
* Map a selector name to a selector id. Shortcut for accessing the selector cache.
*/
#define SELECTOR(_slc_) (g_sci->getKernel()->_selectorCache._slc_)
-//#define SELECTOR(_slc_) _slc_
/**
* Retrieves a selector from an object.
@@ -58,8 +167,8 @@ enum SelectorInvocation {
* This macro halts on error. 'selector' must be a selector name registered in vm.h's
* SelectorCache and mapped in script.cpp.
*/
-#define GET_SEL32(segMan, _obj_, _slc_) read_selector(segMan, _obj_, _slc_)
-#define GET_SEL32V(segMan, _obj_, _slc_) (GET_SEL32(segMan, _obj_, _slc_).offset)
+reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId);
+#define readSelectorValue(segMan, _obj_, _slc_) (readSelector(segMan, _obj_, _slc_).offset)
/**
* Writes a selector value to an object.
@@ -70,26 +179,14 @@ enum SelectorInvocation {
* This macro halts on error. 'selector' must be a selector name registered in vm.h's
* SelectorCache and mapped in script.cpp.
*/
-#define PUT_SEL32(segMan, _obj_, _slc_, _val_) write_selector(segMan, _obj_, _slc_, _val_)
-#define PUT_SEL32V(segMan, _obj_, _slc_, _val_) PUT_SEL32(segMan, _obj_, _slc_, make_reg(0, _val_))
-
+void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value);
+#define writeSelectorValue(segMan, _obj_, _slc_, _val_) writeSelector(segMan, _obj_, _slc_, make_reg(0, _val_))
/**
- * Kludge for use with invoke_selector(). Used for compatibility with compilers
- * that cannot handle vararg macros.
+ * Invokes a selector from an object.
*/
-#define INV_SEL(s, _object_, _selector_, _noinvalid_) \
- s, _object_, g_sci->getKernel()->_selectorCache._selector_, _noinvalid_, argc, argv
-
-
-reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id);
-void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value);
-int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
- int k_argc, StackPtr k_argp, int argc, ...);
-int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
- int k_argc, StackPtr k_argp, int argc, const reg_t *argv);
-
-
+void invokeSelector(EngineState *s, reg_t object, int selectorId,
+ int k_argc, StackPtr k_argp, int argc = 0, const reg_t *argv = 0);
} // End of namespace Sci
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index bd78639c77..a069344d61 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -29,6 +29,7 @@
#include "sci/debug.h" // for g_debug_sleeptime_factor
#include "sci/event.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -69,70 +70,102 @@ static const uint16 s_halfWidthSJISMap[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-EngineState::EngineState(Vocabulary *voc, SegManager *segMan)
-: _voc(voc), _segMan(segMan), _dirseeker() {
+EngineState::EngineState(SegManager *segMan)
+: _segMan(segMan), _dirseeker() {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- sfx_init_flags = 0;
-#endif
+ reset(false);
+}
- restarting_flags = 0;
+EngineState::~EngineState() {
+ delete _msgState;
+}
- last_wait_time = 0;
+void EngineState::reset(bool isRestoring) {
+ if (!isRestoring) {
+ _memorySegmentSize = 0;
- _fileHandles.resize(5);
+ _fileHandles.resize(5);
- execution_stack_base = 0;
- _executionStackPosChanged = false;
-
- r_acc = NULL_REG;
- restAdjust = 0;
- r_prev = NULL_REG;
+ abortScriptProcessing = kAbortNone;
+ }
+ executionStackBase = 0;
+ _executionStackPosChanged = false;
stack_base = 0;
stack_top = 0;
- script_000 = 0;
-
- sys_strings_segment = 0;
- sys_strings = 0;
+ restAdjust = 0;
- _gameObj = NULL_REG;
+ r_acc = NULL_REG;
+ r_prev = NULL_REG;
- gc_countdown = 0;
+ lastWaitTime = 0;
- successor = 0;
+ gcCountDown = 0;
_throttleCounter = 0;
_throttleLastTime = 0;
_throttleTrigger = false;
- _memorySegmentSize = 0;
+ _lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
+ _lastSaveNewId = 0;
- _soundCmd = 0;
+ scriptStepCounter = 0;
+ scriptGCInterval = GC_INTERVAL;
}
-EngineState::~EngineState() {
- delete _msgState;
+void EngineState::speedThrottler(uint32 neededSleep) {
+ if (_throttleTrigger) {
+ uint32 curTime = g_system->getMillis();
+ uint32 duration = curTime - _throttleLastTime;
+
+ if (duration < neededSleep) {
+ g_sci->sleep(neededSleep - duration);
+ _throttleLastTime = g_system->getMillis();
+ } else {
+ _throttleLastTime = curTime;
+ }
+ _throttleTrigger = false;
+ }
}
void EngineState::wait(int16 ticks) {
- uint32 time;
-
- time = g_system->getMillis();
- r_acc = make_reg(0, ((long)time - (long)last_wait_time) * 60 / 1000);
- last_wait_time = time;
+ uint32 time = g_system->getMillis();
+ r_acc = make_reg(0, ((long)time - (long)lastWaitTime) * 60 / 1000);
+ lastWaitTime = time;
ticks *= g_debug_sleeptime_factor;
- _event->sleep(ticks * 1000 / 60);
+ g_sci->sleep(ticks * 1000 / 60);
+}
+
+void EngineState::initGlobals() {
+ Script *script_000 = _segMan->getScript(1);
+
+ if (!script_000->_localsBlock)
+ error("Script 0 has no locals block");
+
+ variablesSegment[VAR_GLOBAL] = script_000->_localsSegment;
+ variablesBase[VAR_GLOBAL] = variables[VAR_GLOBAL] = script_000->_localsBlock->_locals.begin();
+ variablesMax[VAR_GLOBAL] = script_000->_localsBlock->_locals.size();
}
uint16 EngineState::currentRoomNumber() const {
- return script_000->_localsBlock->_locals[13].toUint16();
+ return variables[VAR_GLOBAL][13].toUint16();
}
void EngineState::setRoomNumber(uint16 roomNumber) {
- script_000->_localsBlock->_locals[13] = make_reg(0, roomNumber);
+ variables[VAR_GLOBAL][13] = make_reg(0, roomNumber);
+}
+
+void EngineState::shrinkStackToBase() {
+ if (_executionStack.size() > 0) {
+ uint size = executionStackBase + 1;
+ assert(_executionStack.size() >= size);
+ Common::List<ExecStack>::iterator iter = _executionStack.begin();
+ for (uint i = 0; i < size; ++i)
+ ++iter;
+ _executionStack.erase(iter, _executionStack.end());
+ }
}
static kLanguage charToLanguage(const char c) {
@@ -190,7 +223,7 @@ Common::String SciEngine::getSciLanguageString(const char *str, kLanguage lang,
// Copy double-byte character
char c2 = *(++seeker);
if (!c2) {
- warning("SJIS character %02X is missing second byte", c);
+ error("SJIS character %02X is missing second byte", c);
break;
}
fullWidth += c;
@@ -217,8 +250,8 @@ kLanguage SciEngine::getSciLanguage() {
lang = K_LANG_ENGLISH;
- if (_kernel->_selectorCache.printLang != -1) {
- lang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang));
+ if (SELECTOR(printLang) != -1) {
+ lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(printLang));
if ((getSciVersion() >= SCI_VERSION_1_1) || (lang == K_LANG_NONE)) {
// If language is set to none, we use the language from the game detector.
@@ -251,21 +284,27 @@ kLanguage SciEngine::getSciLanguage() {
default:
lang = K_LANG_ENGLISH;
}
-
- // Store language in printLang selector
- PUT_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang);
}
}
return lang;
}
+void SciEngine::setSciLanguage(kLanguage lang) {
+ if (SELECTOR(printLang) != -1)
+ writeSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(printLang), lang);
+}
+
+void SciEngine::setSciLanguage() {
+ setSciLanguage(getSciLanguage());
+}
+
Common::String SciEngine::strSplit(const char *str, const char *sep) {
kLanguage lang = getSciLanguage();
kLanguage subLang = K_LANG_NONE;
- if (_kernel->_selectorCache.subtitleLang != -1) {
- subLang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang));
+ if (SELECTOR(subtitleLang) != -1) {
+ subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(subtitleLang));
}
kLanguage secondLang;
@@ -285,4 +324,17 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) {
return retval;
}
+void SciEngine::checkVocabularySwitch() {
+ uint16 parserLanguage = 1;
+ if (SELECTOR(parseLang) != -1)
+ parserLanguage = readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(parseLang));
+
+ if (parserLanguage != _vocabularyLanguage) {
+ delete _vocabulary;
+ _vocabulary = new Vocabulary(_resMan, parserLanguage > 1 ? true : false);
+ _vocabulary->reset();
+ _vocabularyLanguage = parserLanguage;
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index c4b995806f..4f1d686b17 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -41,17 +41,21 @@ namespace Common {
#include "sci/parser/vocabulary.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/core.h"
-#endif
#include "sci/sound/soundcmd.h"
namespace Sci {
-class SciEvent;
+class EventManager;
class MessageState;
class SoundCommandParser;
+enum AbortGameState {
+ kAbortNone = 0,
+ kAbortLoadGame = 1,
+ kAbortRestartGame = 2,
+ kAbortQuitGame = 3
+};
+
class DirSeeker {
protected:
reg_t _outbuffer;
@@ -72,12 +76,21 @@ enum {
MAX_SAVE_DIR_SIZE = MAXPATHLEN
};
-/** values for EngineState.restarting_flag */
enum {
- SCI_GAME_IS_NOT_RESTARTING = 0,
- SCI_GAME_WAS_RESTARTED = 1,
- SCI_GAME_IS_RESTARTING_NOW = 2,
- SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE = 4
+ MAX_SAVEGAME_NR = 20 /**< Maximum number of savegames */
+};
+
+// We assume that scripts give us savegameId 0->999 for creating a new save slot
+// and savegameId 1000->1999 for existing save slots ffs. kfile.cpp
+enum {
+ SAVEGAMEID_OFFICIALRANGE_START = 1000,
+ SAVEGAMEID_OFFICIALRANGE_END = 1999
+};
+
+enum {
+ GAMEISRESTARTING_NONE = 0,
+ GAMEISRESTARTING_RESTART = 1,
+ GAMEISRESTARTING_RESTORE = 2
};
class FileHandle {
@@ -96,32 +109,21 @@ public:
struct EngineState : public Common::Serializable {
public:
- EngineState(Vocabulary *voc, SegManager *segMan);
+ EngineState(SegManager *segMan);
virtual ~EngineState();
virtual void saveLoadWithSerializer(Common::Serializer &ser);
public:
SegManager *_segMan; /**< The segment manager */
- Vocabulary *_voc;
-
- Common::String _gameId; /**< Designation of the primary object (which inherits from Game) */
/* Non-VM information */
- SciEvent *_event; // Event handling
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SfxState _sound; /**< sound subsystem */
- int sfx_init_flags; /**< flags the sfx subsystem was initialised with */
-#endif
- SoundCommandParser *_soundCmd;
-
- byte restarting_flags; /**< Flags used for restarting */
-
- uint32 game_start_time; /**< The time at which the interpreter was started */
- uint32 last_wait_time; /**< The last time the game invoked Wait() */
+ uint32 gameStartTime; /**< The time at which the interpreter was started */
+ uint32 lastWaitTime; /**< The last time the game invoked Wait() */
+ uint32 _screenUpdateTime; /**< The last time the game updated the screen */
+ void speedThrottler(uint32 neededSleep);
void wait(int16 ticks);
uint32 _throttleCounter; /**< total times kAnimate was invoked */
@@ -134,6 +136,9 @@ public:
DirSeeker _dirseeker;
+ uint _lastSaveVirtualId; // last virtual id fed to kSaveGame, if no kGetSaveFiles was called inbetween
+ uint _lastSaveNewId; // last newly created filename-id by kSaveGame
+
public:
/* VM Information */
@@ -142,28 +147,44 @@ public:
* When called from kernel functions, the vm is re-started recursively on
* the same stack. This variable contains the stack base for the current vm.
*/
- int execution_stack_base;
+ int executionStackBase;
bool _executionStackPosChanged; /**< Set to true if the execution stack position should be re-evaluated by the vm */
reg_t r_acc; /**< Accumulator */
- int16 restAdjust; /**< &rest register (only used for save games) */
+ int16 restAdjust; /**< current &rest register */
reg_t r_prev; /**< previous comparison result */
StackPtr stack_base; /**< Pointer to the least stack element */
StackPtr stack_top; /**< First invalid stack element */
- Script *script_000; /**< script 000, e.g. for globals */
+ // Script state
+ ExecStack *xs;
+ reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers
+ reg_t *variablesBase[4]; ///< Used for referencing VM ops
+ SegmentId variablesSegment[4]; ///< Same as above, contains segment IDs
+ int variablesMax[4]; ///< Max. values for all variables
+
+ AbortGameState abortScriptProcessing;
+ int16 gameIsRestarting; // is set when restarting (=1) or restoring the game (=2)
+
+ int scriptStepCounter; // Counts the number of steps executed
+ int scriptGCInterval; // Number of steps in between gcs
uint16 currentRoomNumber() const;
void setRoomNumber(uint16 roomNumber);
- /* System strings */
- SegmentId sys_strings_segment;
- SystemStrings *sys_strings;
+ /**
+ * Sets global variables from script 0
+ */
+ void initGlobals();
- reg_t _gameObj; /**< Pointer to the game object */
+ /**
+ * Shrink execution stack to size.
+ * Contains an assert if it is not already smaller.
+ */
+ void shrinkStackToBase();
- int gc_countdown; /**< Number of kernel calls until next gc */
+ int gcCountDown; /**< Number of kernel calls until next gc */
public:
MessageState *_msgState;
@@ -176,7 +197,10 @@ public:
uint _memorySegmentSize;
byte _memorySegment[kMemorySegmentMax];
- EngineState *successor; /**< Successor of this state: Used for restoring */
+ /**
+ * Resets the engine state.
+ */
+ void reset(bool isRestoring);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index 0f558f2dc8..aae6de01f1 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -38,75 +38,86 @@ struct SelectorRemap {
};
static const char * const sci0Selectors[] = {
- "y", "x", "view", "loop", "cel", // 0 - 4
- "underBits", "nsTop", "nsLeft", "nsBottom", "nsRight", // 5 - 9
- "lsTop", "lsLeft", "lsBottom", "lsRight", "signal", // 10 - 14
- "illegalBits", "brTop", "brLeft", "brBottom", "brRight", // 15 - 19
- "name", "key", "time", "text", "elements", // 20 - 25
- "color", "back", "mode", "style", "state", // 25 - 29
- "font", "type", "window", "cursor", "max", // 30 - 34
- "mark", "who", "message", "edit", "play", // 35 - 39
- "number", "handle", "client", "dx", "dy", // 40 - 44
- "b-moveCnt", "b-i1", "b-i2", "b-di", "b-xAxis", // 45 - 49
- "b-incr", "xStep", "yStep", "moveSpeed", "canBeHere", // 50 - 54
- "heading", "mover", "doit", "isBlocked", "looper", // 55 - 59
- "priority", "modifiers", "replay", "setPri", "at", // 60 - 64
- "next", "done", "width", "wordFail", "syntaxFail", // 65 - 69
- "semanticFail", "pragmaFail", "said", "claimed", "value", // 70 - 74
- "save", "restore", "title", "button", "icon", // 75 - 79
- "draw", "delete", "z" // 80 - 82
+ "y", "x", "view", "loop", "cel", // 0 - 4
+ "underBits", "nsTop", "nsLeft", "nsBottom", "nsRight", // 5 - 9
+ "lsTop", "lsLeft", "lsBottom", "lsRight", "signal", // 10 - 14
+ "illegalBits", "brTop", "brLeft", "brBottom", "brRight", // 15 - 19
+ "name", "key", "time", "text", "elements", // 20 - 25
+ "color", "back", "mode", "style", "state", // 25 - 29
+ "font", "type", "window", "cursor", "max", // 30 - 34
+ "mark", "who", "message", "edit", "play", // 35 - 39
+ "number", "handle", "client", "dx", "dy", // 40 - 44
+ "b-moveCnt", "b-i1", "b-i2", "b-di", "b-xAxis", // 45 - 49
+ "b-incr", "xStep", "yStep", "moveSpeed", "canBeHere", // 50 - 54
+ "heading", "mover", "doit", "isBlocked", "looper", // 55 - 59
+ "priority", "modifiers", "replay", "setPri", "at", // 60 - 64
+ "next", "done", "width", "wordFail", "syntaxFail", // 65 - 69
+ "semanticFail", "pragmaFail", "said", "claimed", "value", // 70 - 74
+ "save", "restore", "title", "button", "icon", // 75 - 79
+ "draw", "delete", "z" // 80 - 82
};
static const char * const sci1Selectors[] = {
- "parseLang", "printLang", "subtitleLang", "size", "points", // 83 - 87
- "palette", "dataInc", "handle", "min", "sec", // 88 - 92
- "frame", "vol", "pri", "perform", "moveDone" // 93 - 97
+ "parseLang", "printLang", "subtitleLang", "size", "points", // 83 - 87
+ "palette", "dataInc", "handle", "min", "sec", // 88 - 92
+ "frame", "vol", "pri", "perform", "moveDone" // 93 - 97
};
#ifdef ENABLE_SCI32
static const char * const sci2Selectors[] = {
- "plane", "x", "y", "z", "scaleX", // 0 - 4
- "scaleY", "maxScale", "priority", "fixPriority", "inLeft", // 5 - 9
- "inTop", "inRight", "inBottom", "useInsetRect", "view", // 10 - 14
- "loop", "cel", "bitmap", "nsLeft", "nsTop", // 15 - 19
- "nsRight", "nsBottom", "lsLeft", "lsTop", "lsRight", // 20 - 25
- "lsBottom", "signal", "illegalBits", "brLeft", "brTop", // 25 - 29
- "brRight", "brBottom", "name", "key", "time", // 30 - 34
- "text", "elements", "fore", "back", "mode", // 35 - 39
- "style", "state", "font", "type", "window", // 40 - 44
- "cursor", "max", "mark", "who", "message", // 45 - 49
- "edit", "play", "number", "nodePtr", "client", // 50 - 54
- "dx", "dy", "b-moveCnt", "b-i1", "b-i2", // 55 - 59
- "b-di", "b-xAxis", "b-incr", "xStep", "yStep", // 60 - 64
- "moveSpeed", "cantBeHere", "heading", "mover", "doit", // 65 - 69
- "isBlocked", "looper", "modifiers", "replay", "setPri", // 70 - 74
- "at", "next", "done", "width", "pragmaFail", // 75 - 79
- "claimed", "value", "save", "restore", "title", // 80 - 84
- "button", "icon", "draw", "delete", "printLang", // 85 - 89
- "size", "points", "palette", "dataInc", "handle", // 90 - 94
- "min", "sec", "frame", "vol", "perform", // 95 - 99
- "moveDone", "topString", "flags", "quitGame", "restart", // 100 - 104
- "hide", "scaleSignal", "vanishingX", "vanishingY", "picture", // 105 - 109
- "resX", "resY", "coordType", "data", "skip", // 110 - 104
- "center", "all", "show", "textLeft", "textTop", // 115 - 119
- "textRight", "textBottom", "borderColor", "titleFore", "titleBack", // 120 - 124
- "titleFont", "dimmed", "frameOut", "lastKey", "magnifier", // 125 - 129
- "magPower", "mirrored", "pitch", "roll", "yaw", // 130 - 134
- "left", "right", "top", "bottom", "numLines" // 135 - 139
+ "plane", "x", "y", "z", "scaleX", // 0 - 4
+ "scaleY", "maxScale", "priority", "fixPriority", "inLeft", // 5 - 9
+ "inTop", "inRight", "inBottom", "useInsetRect", "view", // 10 - 14
+ "loop", "cel", "bitmap", "nsLeft", "nsTop", // 15 - 19
+ "nsRight", "nsBottom", "lsLeft", "lsTop", "lsRight", // 20 - 25
+ "lsBottom", "signal", "illegalBits", "brLeft", "brTop", // 25 - 29
+ "brRight", "brBottom", "name", "key", "time", // 30 - 34
+ "text", "elements", "fore", "back", "mode", // 35 - 39
+ "style", "state", "font", "type", "window", // 40 - 44
+ "cursor", "max", "mark", "who", "message", // 45 - 49
+ "edit", "play", "number", "nodePtr", "client", // 50 - 54
+ "dx", "dy", "b-moveCnt", "b-i1", "b-i2", // 55 - 59
+ "b-di", "b-xAxis", "b-incr", "xStep", "yStep", // 60 - 64
+ "moveSpeed", "cantBeHere", "heading", "mover", "doit", // 65 - 69
+ "isBlocked", "looper", "modifiers", "replay", "setPri", // 70 - 74
+ "at", "next", "done", "width", "pragmaFail", // 75 - 79
+ "claimed", "value", "save", "restore", "title", // 80 - 84
+ "button", "icon", "draw", "delete", "printLang", // 85 - 89
+ "size", "points", "palette", "dataInc", "handle", // 90 - 94
+ "min", "sec", "frame", "vol", "perform", // 95 - 99
+ "moveDone", "topString", "flags", "quitGame", "restart", // 100 - 104
+ "hide", "scaleSignal", "vanishingX", "vanishingY", "picture", // 105 - 109
+ "resX", "resY", "coordType", "data", "skip", // 110 - 104
+ "center", "all", "show", "textLeft", "textTop", // 115 - 119
+ "textRight", "textBottom", "borderColor", "titleFore", "titleBack", // 120 - 124
+ "titleFont", "dimmed", "frameOut", "lastKey", "magnifier", // 125 - 129
+ "magPower", "mirrored", "pitch", "roll", "yaw", // 130 - 134
+ "left", "right", "top", "bottom", "numLines" // 135 - 139
};
#endif
static const SelectorRemap sciSelectorRemap[] = {
- { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "moveDone", 170 },
+ { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "moveDone", 170 },
{ SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "points", 316 },
{ SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "flags", 368 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_1, "nodePtr", 44 },
- { SCI_VERSION_1_LATE, SCI_VERSION_1_1, "cantBeHere", 57 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_1, "topString", 101 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_1, "flags", 102 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "nodePtr", 44 },
+ { SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, "cantBeHere", 57 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "topString", 101 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "flags", 102 },
+ // SCI1.1
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "nodePtr", 41 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "cantBeHere", 54 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "topString", 98 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "flags", 99 },
+ // quitGame
+ // restart
+ // hide
{ SCI_VERSION_1_1, SCI_VERSION_1_1,"scaleSignal", 103 },
{ SCI_VERSION_1_1, SCI_VERSION_1_1, "scaleX", 104 },
{ SCI_VERSION_1_1, SCI_VERSION_1_1, "scaleY", 105 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "maxScale", 106 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "vanishingX", 107 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "vanishingY", 108 },
{ SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
};
@@ -139,16 +150,41 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
}
for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) {
- uint32 slot = selectorRemap->slot;
- if (selectorRemap->slot >= names.size())
- names.resize(selectorRemap->slot + 1);
if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) {
- // The SCI1 selectors we use exist in SCI1.1 too, offset by 3
- if (selectorRemap->minVersion >= SCI_VERSION_1_EARLY && getSciVersion() == SCI_VERSION_1_1)
- slot -= 3;
+ const uint32 slot = selectorRemap->slot;
+ if (slot >= names.size())
+ names.resize(slot + 1);
names[slot] = selectorRemap->name;
}
}
+
+ if (g_sci->getGameId() == GID_HOYLE4) {
+ // The demo of Hoyle 4 is one of the few demos with lip syncing and no selector vocabulary.
+ // This needs two selectors, "syncTime" and "syncCue", which keep changing positions in each
+ // game. Usually, games with speech and lip sync have a selector vocabulary, so we don't need
+ // to set these two selectors, but we need for Hoyle...
+ if (names.size() < 276)
+ names.resize(276);
+
+ names[274] = "syncTime";
+ names[275] = "syncCue";
+ } else if (g_sci->getGameId() == GID_ISLANDBRAIN) {
+ // The demo of Island of Dr. Brain needs the init selector set to match up with the full
+ // game's workaround - bug #3035033
+ if (names.size() < 111)
+ names.resize(111);
+
+ names[110] = "init";
+ } else if (g_sci->getGameId() == GID_LAURABOW2) {
+ // The floppy of version needs the open and changeState selectors set to match up with the
+ // CD version's workarounds - bugs #3035694 and #3036291
+ if (names.size() < 190)
+ names.resize(190);
+
+ names[144] = "changeState";
+ names[189] = "open";
+ }
+
#ifdef ENABLE_SCI32
} else {
// SCI2+
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index e2ee2e1971..7c989e43f4 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -30,29 +30,23 @@
#include "sci/sci.h"
#include "sci/console.h"
-#include "sci/debug.h" // for g_debugState
#include "sci/resource.h"
#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
-#include "sci/engine/seg_manager.h"
#include "sci/engine/script.h"
+#include "sci/engine/seg_manager.h"
+#include "sci/engine/selector.h" // for SELECTOR
#include "sci/engine/gc.h"
+#include "sci/engine/workarounds.h"
namespace Sci {
const reg_t NULL_REG = {0, 0};
const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
-
+const reg_t TRUE_REG = {0, 1};
//#define VM_DEBUG_SEND
-ScriptState scriptState; // FIXME: Avoid non-const global vars
-int g_loadFromLauncher; // FIXME: Avoid non-const global vars
-
-int script_abort_flag = 0; // Set to 1 to abort execution. Set to 2 to force a replay afterwards // FIXME: Avoid non-const global vars
-int script_step_counter = 0; // Counts the number of steps executed // FIXME: Avoid non-const global vars
-int script_gc_interval = GC_INTERVAL; // Number of steps in between gcs // FIXME: Avoid non-const global vars
-
#define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
/**
@@ -66,6 +60,8 @@ int script_gc_interval = GC_INTERVAL; // Number of steps in between gcs // FIXME
* @param[in] argp Heap pointer to the first parameter
* @param[in] selector The selector by which it was called or
* NULL_SELECTOR if n.a. For debugging.
+ * @param[in] exportId The exportId by which it was called or
+ * -1 if n.a. For debugging.
* @param[in] sendp Pointer to the object which the message was
* sent to. Equal to objp for anything but super.
* @param[in] origin Number of the execution stack element this
@@ -76,10 +72,9 @@ int script_gc_interval = GC_INTERVAL; // Number of steps in between gcs // FIXME
* @return A pointer to the new exec stack TOS entry
*/
static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t pc, StackPtr sp,
- reg_t objp, int argc, StackPtr argp, Selector selector,
+ reg_t objp, int argc, StackPtr argp, Selector selector, int exportId, int localCallOffset,
reg_t sendp, int origin, SegmentId local_segment);
-
/**
* Adds one varselector access to the execution stack.
* This function is called from send_selector only.
@@ -97,30 +92,28 @@ static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack,
int origin);
-
-
// validation functionality
-#ifndef DISABLE_VALIDATIONS
-
static reg_t &validate_property(Object *obj, int index) {
// A static dummy reg_t, which we return if obj or index turn out to be
// invalid. Note that we cannot just return NULL_REG, because client code
// may modify the value of the returned reg_t.
static reg_t dummyReg = NULL_REG;
- if (!obj) {
- debugC(2, kDebugLevelVM, "[VM] Sending to disposed object!");
- return dummyReg;
- }
+ // If this occurs, it means there's probably something wrong with the garbage
+ // collector, so don't hide it with fake return values
+ if (!obj)
+ error("validate_property: Sending to disposed object");
if (index < 0 || (uint)index >= obj->getVarCount()) {
+ // This is same way sierra does it and there are some games, that contain such scripts like
+ // iceman script 998 (fred::canBeHere, executed right at the start)
debugC(2, kDebugLevelVM, "[VM] Invalid property #%d (out of [0..%d]) requested!",
index, obj->getVarCount());
return dummyReg;
}
- return obj->_variables[index];
+ return obj->getVariableRef(index);
}
static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) {
@@ -134,7 +127,9 @@ static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) {
static int validate_arithmetic(reg_t reg) {
if (reg.segment) {
- warning("[VM] Attempt to read arithmetic value from non-zero segment [%04x]", reg.segment);
+ // The results of this are likely unpredictable... It most likely means that a kernel function is returning something wrong.
+ // If such an error occurs, we usually need to find the last kernel function called and check its return value.
+ error("[VM] Attempt to read arithmetic value from non-zero segment [%04x]. Address: %04x:%04x", reg.segment, PRINT_REG(reg));
return 0;
}
@@ -142,15 +137,10 @@ static int validate_arithmetic(reg_t reg) {
}
static int signed_validate_arithmetic(reg_t reg) {
- if (reg.segment) {
- warning("[VM] Attempt to read arithmetic value from non-zero segment [%04x]", reg.segment);
- return 0;
- }
-
- return (int16)reg.offset;
+ return (int16)validate_arithmetic(reg);
}
-static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index, int line) {
+static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index) {
const char *names[4] = {"global", "local", "temp", "param"};
if (index < 0 || index >= max) {
@@ -165,8 +155,8 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
if (type == VAR_PARAM || type == VAR_TEMP) {
int total_offset = r - stack_base;
if (total_offset < 0 || total_offset >= VM_STACK_SIZE) {
- warning("%s", txt.c_str());
- warning("[VM] Access would be outside even of the stack (%d); access denied", total_offset);
+ // Fatal, as the game is trying to do an OOB access
+ error("%s. [VM] Access would be outside even of the stack (%d); access denied", txt.c_str(), total_offset);
return false;
} else {
debugC(2, kDebugLevelVM, "%s", txt.c_str());
@@ -180,15 +170,67 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
return true;
}
-static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t default_value) {
- if (validate_variable(r, stack_base, type, max, index, line))
+static bool validate_unsignedInteger(reg_t reg, uint16 &integer) {
+ if (reg.segment)
+ return false;
+ integer = reg.offset;
+ return true;
+}
+
+static bool validate_signedInteger(reg_t reg, int16 &integer) {
+ if (reg.segment)
+ return false;
+ integer = (int16)reg.offset;
+ return true;
+}
+
+extern const char *opcodeNames[]; // from scriptdebug.cpp
+
+static reg_t arithmetic_lookForWorkaround(const byte opcode, const SciWorkaroundEntry *workaroundList, reg_t value1, reg_t value2) {
+ SciTrackOriginReply originReply;
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, workaroundList, &originReply);
+ if (solution.type == WORKAROUND_NONE)
+ error("%s on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)",
+ opcodeNames[opcode], PRINT_REG(value1), PRINT_REG(value2), originReply.objectName.c_str(),
+ originReply.methodName.c_str(), originReply.scriptNr, g_sci->getEngineState()->currentRoomNumber(),
+ originReply.localCallOffset);
+ assert(solution.type == WORKAROUND_FAKE);
+ return make_reg(0, solution.value);
+}
+
+static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, reg_t default_value) {
+ if (validate_variable(r, stack_base, type, max, index)) {
+ if (r[index].segment == 0xffff) {
+ switch (type) {
+ case VAR_TEMP: {
+ // Uninitialized read on a temp
+ // We need to find correct replacements for each situation manually
+ SciTrackOriginReply originReply;
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(index, uninitializedReadWorkarounds, &originReply);
+ if (solution.type == WORKAROUND_NONE)
+ error("Uninitialized read for temp %d from method %s::%s (script %d, room %d, localCall %x)",
+ index, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr,
+ g_sci->getEngineState()->currentRoomNumber(), originReply.localCallOffset);
+ assert(solution.type == WORKAROUND_FAKE);
+ r[index] = make_reg(0, solution.value);
+ break;
+ }
+ case VAR_PARAM:
+ // Out-of-bounds read for a parameter that goes onto stack and hits an uninitialized temp
+ // We return 0 currently in that case
+ debugC(2, kDebugLevelVM, "[VM] Read for a parameter goes out-of-bounds, onto the stack and gets uninitialized temp");
+ return NULL_REG;
+ default:
+ break;
+ }
+ }
return r[index];
- else
+ } else
return default_value;
}
-static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t value, SegManager *segMan, Kernel *kernel) {
- if (validate_variable(r, stack_base, type, max, index, line)) {
+static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, reg_t value, SegManager *segMan, Kernel *kernel) {
+ if (validate_variable(r, stack_base, type, max, index)) {
// WORKAROUND: This code is needed to work around a probable script bug, or a
// limitation of the original SCI engine, which can be observed in LSL5.
@@ -207,59 +249,66 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i
// stopGroop object, which points to ego, to the new ego object. If this is not
// done, ego's movement will not be updated properly, so the result is
// unpredictable (for example in LSL5, Patti spins around instead of walking).
- if (index == 0 && type == VAR_GLOBAL) { // global 0 is ego
+ if (index == 0 && type == VAR_GLOBAL && getSciVersion() > SCI_VERSION_0_EARLY) { // global 0 is ego
reg_t stopGroopPos = segMan->findObjectByName("stopGroop");
if (!stopGroopPos.isNull()) { // does the game have a stopGroop object?
// Find the "client" member variable of the stopGroop object, and update it
ObjVarRef varp;
- if (lookup_selector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) {
+ if (lookupSelector(segMan, stopGroopPos, SELECTOR(client), &varp, NULL) == kSelectorVariable) {
reg_t *clientVar = varp.getPointer(segMan);
*clientVar = value;
}
}
}
+ // If we are writing an uninitialized value into a temp, we remove the uninitialized segment
+ // this happens at least in sq1/room 44 (slot-machine), because a send is missing parameters, then
+ // those parameters are taken from uninitialized stack and afterwards they are copied back into temps
+ // if we don't remove the segment, we would get false-positive uninitialized reads later
+ if (type == VAR_TEMP && value.segment == 0xffff)
+ value.segment = 0;
+
r[index] = value;
}
}
-#else
-// Non-validating alternatives
-
-# define validate_stack_addr(s, sp) sp
-# define validate_arithmetic(r) ((r).offset)
-# define signed_validate_arithmetic(r) ((int16)(r).offset)
-# define validate_variable(r, sb, t, m, i, l)
-# define validate_read_var(r, sb, t, m, i, l, dv) ((r)[i])
-# define validate_write_var(r, sb, t, m, i, l, v, sm, k) ((r)[i] = (v))
-# define validate_property(o, p) ((o)->_variables[p])
-
-#endif
-
-#define READ_VAR(type, index, def) validate_read_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, def)
-#define WRITE_VAR(type, index, value) validate_write_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, value, s->_segMan, g_sci->getKernel())
+#define READ_VAR(type, index) validate_read_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, s->r_acc)
+#define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, value, s->_segMan, g_sci->getKernel())
#define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value));
-#define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc)))
-#define ACC_AUX_LOAD() aux_acc = signed_validate_arithmetic(s->r_acc)
-#define ACC_AUX_STORE() s->r_acc = make_reg(0, aux_acc)
-
-#define OBJ_PROPERTY(o, p) (validate_property(o, p))
-
// Operating on the stack
// 16 bit:
#define PUSH(v) PUSH32(make_reg(0, v))
-#define POP() (validate_arithmetic(POP32()))
// 32 bit:
-#define PUSH32(a) (*(validate_stack_addr(s, (scriptState.xs->sp)++)) = (a))
-#define POP32() (*(validate_stack_addr(s, --(scriptState.xs->sp))))
+#define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a))
+#define POP32() (*(validate_stack_addr(s, --(s->xs->sp))))
+
+bool SciEngine::checkExportBreakpoint(uint16 script, uint16 pubfunct) {
+ if (_debugState._activeBreakpointTypes & BREAK_EXPORT) {
+ uint32 bpaddress;
+
+ bpaddress = (script << 16 | pubfunct);
+
+ Common::List<Breakpoint>::const_iterator bp;
+ for (bp = _debugState._breakpoints.begin(); bp != _debugState._breakpoints.end(); ++bp) {
+ if (bp->type == BREAK_EXPORT && bp->address == bpaddress) {
+ _console->DebugPrintf("Break on script %d, export %d\n", script, pubfunct);
+ _debugState.debugging = true;
+ _debugState.breakpointWasHit = true;
+ return true;;
+ }
+ }
+ }
+
+ return false;
+}
ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackPtr sp, reg_t calling_obj, uint16 argc, StackPtr argp) {
int seg = s->_segMan->getScriptSegment(script);
Script *scr = s->_segMan->getScriptIfLoaded(seg);
if (!scr || scr->isMarkedAsDeleted()) { // Script not present yet?
- seg = script_instantiate(g_sci->getResMan(), s->_segMan, script);
+ seg = s->_segMan->instantiateScript(script);
scr = s->_segMan->getScript(seg);
}
@@ -277,24 +326,9 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
}
// Check if a breakpoint is set on this method
- if (g_debugState._activeBreakpointTypes & BREAK_EXPORT) {
- uint32 bpaddress;
-
- bpaddress = (script << 16 | pubfunct);
-
- Common::List<Breakpoint>::const_iterator bp;
- for (bp = g_debugState._breakpoints.begin(); bp != g_debugState._breakpoints.end(); ++bp) {
- if (bp->type == BREAK_EXPORT && bp->address == bpaddress) {
- Console *con = g_sci->getSciDebugger();
- con->DebugPrintf("Break on script %d, export %d\n", script, pubfunct);
- g_debugState.debugging = true;
- g_debugState.breakpointWasHit = true;
- break;
- }
- }
- }
+ g_sci->checkExportBreakpoint(script, pubfunct);
- return add_exec_stack_entry(s->_executionStack, make_reg(seg, temp), sp, calling_obj, argc, argp, -1, calling_obj, s->_executionStack.size()-1, seg);
+ return add_exec_stack_entry(s->_executionStack, make_reg(seg, temp), sp, calling_obj, argc, argp, -1, pubfunct, -1, calling_obj, s->_executionStack.size()-1, seg);
}
@@ -304,7 +338,7 @@ static void _exec_varselectors(EngineState *s) {
ExecStack &xs = s->_executionStack.back();
reg_t *var = xs.getVarPointer(s->_segMan);
if (!var) {
- warning("Invalid varselector exec stack entry");
+ error("Invalid varselector exec stack entry");
} else {
// varselector access?
if (xs.argc) { // write?
@@ -332,6 +366,30 @@ struct CallsStruct {
int type; /**< Same as ExecStack.type */
};
+bool SciEngine::checkSelectorBreakpoint(reg_t send_obj, int selector) {
+ if (_debugState._activeBreakpointTypes & BREAK_SELECTOR) {
+ char method_name[256];
+
+ sprintf(method_name, "%s::%s", _gamestate->_segMan->getObjectName(send_obj), getKernel()->getSelectorName(selector).c_str());
+
+ Common::List<Breakpoint>::const_iterator bp;
+ for (bp = _debugState._breakpoints.begin(); bp != _debugState._breakpoints.end(); ++bp) {
+ int cmplen = bp->name.size();
+ if (bp->name.lastChar() != ':')
+ cmplen = 256;
+
+ if (bp->type == BREAK_SELECTOR && !strncmp(bp->name.c_str(), method_name, cmplen)) {
+ _console->DebugPrintf("Break on %s (in [%04x:%04x])\n", method_name, PRINT_REG(send_obj));
+ _debugState.debugging = true;
+ _debugState.breakpointWasHit = true;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPtr sp, int framesize, StackPtr argp) {
// send_obj and work_obj are equal for anything but 'super'
// Returns a pointer to the TOS exec_stack element
@@ -341,7 +399,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
int selector;
int argc;
int origin = s->_executionStack.size()-1; // Origin: Used for debugging
- int print_send_action = 0;
+ bool printSendActions = false;
// We return a pointer to the new active ExecStack
// The selector calls we catch are stored below:
@@ -356,34 +414,16 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
// Check if a breakpoint is set on this method
- if (g_debugState._activeBreakpointTypes & BREAK_SELECTOR) {
- char method_name[256];
-
- sprintf(method_name, "%s::%s", s->_segMan->getObjectName(send_obj), g_sci->getKernel()->getSelectorName(selector).c_str());
-
- Common::List<Breakpoint>::const_iterator bp;
- for (bp = g_debugState._breakpoints.begin(); bp != g_debugState._breakpoints.end(); ++bp) {
- int cmplen = bp->name.size();
- if (bp->name.lastChar() != ':')
- cmplen = 256;
-
- if (bp->type == BREAK_SELECTOR && !strncmp(bp->name.c_str(), method_name, cmplen)) {
- Console *con = g_sci->getSciDebugger();
- con->DebugPrintf("Break on %s (in [%04x:%04x])\n", method_name, PRINT_REG(send_obj));
- print_send_action = 1;
- g_debugState.debugging = true;
- g_debugState.breakpointWasHit = true;
- break;
- }
- }
- }
+ printSendActions = g_sci->checkSelectorBreakpoint(send_obj, selector);
#ifdef VM_DEBUG_SEND
- printf("Send to %04x:%04x, selector %04x (%s):", PRINT_REG(send_obj), selector, g_sci->getKernel()->getSelectorName(selector).c_str());
+ printf("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj),
+ s->_segMan->getObjectName(send_obj), selector,
+ g_sci->getKernel()->getSelectorName(selector).c_str());
#endif // VM_DEBUG_SEND
ObjVarRef varp;
- switch (lookup_selector(s->_segMan, send_obj, selector, &varp, &funcp)) {
+ switch (lookupSelector(s->_segMan, send_obj, selector, &varp, &funcp)) {
case kSelectorNone:
error("Send to invalid selector 0x%x of object at %04x:%04x", 0xffff & selector, PRINT_REG(send_obj));
break;
@@ -398,22 +438,35 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
#endif // VM_DEBUG_SEND
// argc == 0: read selector
- // argc == 1: write selector
- // argc > 1: write selector?
- if (print_send_action && argc == 0) { // read selector
- printf("[read selector]\n");
- print_send_action = 0;
+ // argc != 0: write selector
+ if (printSendActions && !argc) { // read selector
+ debug("[read selector]\n");
+ printSendActions = false;
}
- if (print_send_action && argc > 0) {
+ if (printSendActions && argc) {
reg_t oldReg = *varp.getPointer(s->_segMan);
reg_t newReg = argp[1];
- printf("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg));
- print_send_action = 0;
+ warning("[write to selector (%s:%s): change %04x:%04x to %04x:%04x]\n",
+ s->_segMan->getObjectName(send_obj), g_sci->getKernel()->getSelectorName(selector).c_str(),
+ PRINT_REG(oldReg), PRINT_REG(newReg));
+ printSendActions = false;
}
- if (argc > 1)
- warning("send_selector(): more than 1 parameter (%d) while modifying a variable selector", argc);
+ if (argc > 1) {
+ // argc can indeed be bigger than 1 in some cases, and it's usually the
+ // result of a script bug. Usually these aren't fatal.
+
+ const char *objectName = s->_segMan->getObjectName(send_obj);
+
+ reg_t oldReg = *varp.getPointer(s->_segMan);
+ reg_t newReg = argp[1];
+ const char *selectorName = g_sci->getKernel()->getSelectorName(selector).c_str();
+ debug(2, "send_selector(): argc = %d while modifying variable selector "
+ "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x",
+ argc, selector, selectorName, PRINT_REG(send_obj),
+ objectName, PRINT_REG(oldReg), PRINT_REG(newReg));
+ }
{
CallsStruct call;
@@ -438,9 +491,30 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
printf(") at %04x:%04x\n", PRINT_REG(funcp));
#endif // VM_DEBUG_SEND
- if (print_send_action) {
- printf("[invoke selector]\n");
- print_send_action = 0;
+ if (printSendActions) {
+ printf("[invoke selector]");
+#ifndef VM_DEBUG_SEND
+ int displaySize = 0;
+ for (int argNr = 1; argNr <= argc; argNr++) {
+ if (argNr == 1)
+ printf(" - ");
+ reg_t curParam = argp[argNr];
+ if (curParam.segment) {
+ printf("[%04x:%04x] ", PRINT_REG(curParam));
+ displaySize += 12;
+ } else {
+ printf("[%04x] ", curParam.offset);
+ displaySize += 7;
+ }
+ if (displaySize > 50) {
+ if (argNr < argc)
+ printf("...");
+ break;
+ }
+ }
+#endif
+ printf("\n");
+ printSendActions = false;
}
{
@@ -456,7 +530,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
break;
- } // switch (lookup_selector())
+ } // switch (lookupSelector())
framesize -= (2 + argc);
argp += argc + 1;
@@ -472,18 +546,16 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
else
add_exec_stack_entry(s->_executionStack, call.address.func, call.sp, work_obj,
call.argc, call.argp,
- call.selector, send_obj, origin, SCI_XS_CALLEE_LOCALS);
+ call.selector, -1, -1, send_obj, origin, SCI_XS_CALLEE_LOCALS);
}
_exec_varselectors(s);
- if (s->_executionStack.empty())
- return NULL;
- return &(s->_executionStack.back());
+ return s->_executionStack.empty() ? NULL : &(s->_executionStack.back());
}
static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack, reg_t objp, int argc, StackPtr argp, Selector selector, const ObjVarRef& address, int origin) {
- ExecStack *xstack = add_exec_stack_entry(execStack, NULL_REG, 0, objp, argc, argp, selector, objp, origin, SCI_XS_CALLEE_LOCALS);
+ ExecStack *xstack = add_exec_stack_entry(execStack, NULL_REG, 0, objp, argc, argp, selector, -1, -1, objp, origin, SCI_XS_CALLEE_LOCALS);
// Store selector address in sp
xstack->addr.varp = address;
@@ -493,7 +565,7 @@ static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack,
}
static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t pc, StackPtr sp, reg_t objp, int argc,
- StackPtr argp, Selector selector, reg_t sendp, int origin, SegmentId _localsSegment) {
+ StackPtr argp, Selector selector, int exportId, int localCallOffset, reg_t sendp, int origin, SegmentId _localsSegment) {
// Returns new TOS element for the execution stack
// _localsSegment may be -1 if derived from the called object
@@ -517,8 +589,10 @@ static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t
*argp = make_reg(0, argc); // SCI code relies on the zeroeth argument to equal argc
// Additional debug information
- xstack.selector = selector;
- xstack.origin = origin;
+ xstack.debugSelector = selector;
+ xstack.debugExportId = exportId;
+ xstack.debugLocalCallOffset = localCallOffset;
+ xstack.debugOrigin = origin;
xstack.type = EXEC_STACK_TYPE_CALL; // Normal call
@@ -526,10 +600,6 @@ static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t
return &(execStack.back());
}
-#ifdef DISABLE_VALIDATIONS
-# define kernel_matches_signature(a, b, c, d) 1
-#endif
-
static reg_t pointer_add(EngineState *s, reg_t base, int offset) {
SegmentObj *mobj = s->_segMan->getSegmentObj(base.segment);
@@ -557,77 +627,187 @@ static reg_t pointer_add(EngineState *s, reg_t base, int offset) {
}
}
-static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) {
-
- if (kernelFuncNum >= (int)g_sci->getKernel()->_kernelFuncs.size())
- error("Invalid kernel function 0x%x requested", kernelFuncNum);
-
- const KernelFuncWithSignature &kernelFunc = g_sci->getKernel()->_kernelFuncs[kernelFuncNum];
+static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc, reg_t *argv) {
+ // Add stack frame to indicate we're executing a callk.
+ // This is useful in debugger backtraces if this
+ // kernel function calls a script itself.
+ ExecStack *xstack;
+ xstack = add_exec_stack_entry(s->_executionStack, NULL_REG, NULL, NULL_REG, argc, argv - 1, 0, -1, -1, NULL_REG,
+ s->_executionStack.size()-1, SCI_XS_CALLEE_LOCALS);
+ xstack->debugSelector = kernelCallNr;
+ xstack->type = EXEC_STACK_TYPE_KERNEL;
+}
- if (kernelFunc.signature
- && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, scriptState.xs->sp + 1)) {
- error("[VM] Invalid arguments to kernel call %x", kernelFuncNum);
+static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *kernelSubCall, EngineState *s, int argc, reg_t *argv, reg_t result) {
+ Kernel *kernel = g_sci->getKernel();
+ if (!kernelSubCall) {
+ printf("k%s: ", kernelCall->name);
+ } else {
+ int callNameLen = strlen(kernelCall->name);
+ if (strncmp(kernelCall->name, kernelSubCall->name, callNameLen) == 0) {
+ const char *subCallName = kernelSubCall->name + callNameLen;
+ printf("k%s(%s): ", kernelCall->name, subCallName);
+ } else {
+ printf("k%s(%s): ", kernelCall->name, kernelSubCall->name);
+ }
}
+ for (int parmNr = 0; parmNr < argc; parmNr++) {
+ if (parmNr)
+ printf(", ");
+ uint16 regType = kernel->findRegType(argv[parmNr]);
+ if (regType & SIG_TYPE_NULL)
+ printf("0");
+ else if (regType & SIG_TYPE_UNINITIALIZED)
+ printf("UNINIT");
+ else if (regType & SIG_IS_INVALID)
+ printf("INVALID");
+ else if (regType & SIG_TYPE_INTEGER)
+ printf("%d", argv[parmNr].offset);
+ else {
+ printf("%04x:%04x", PRINT_REG(argv[parmNr]));
+ switch (regType) {
+ case SIG_TYPE_OBJECT:
+ printf(" (%s)", s->_segMan->getObjectName(argv[parmNr]));
+ break;
+ case SIG_TYPE_REFERENCE:
+ if (kernelCall->function == kSaid) {
+ SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]);
+ if (saidSpec.isRaw) {
+ printf(" ('");
+ g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw);
+ printf("')");
+ } else {
+ printf(" (non-raw said-spec)");
+ }
+ } else {
+ printf(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str());
+ }
+ default:
+ break;
+ }
+ }
+ }
+ if (result.segment)
+ printf(" = %04x:%04x\n", PRINT_REG(result));
+ else
+ printf(" = %d\n", result.offset);
+}
- reg_t *argv = scriptState.xs->sp + 1;
-
- if (!kernelFunc.isDummy) {
- // Add stack frame to indicate we're executing a callk.
- // This is useful in debugger backtraces if this
- // kernel function calls a script itself.
- ExecStack *xstack;
- xstack = add_exec_stack_entry(s->_executionStack, NULL_REG, NULL, NULL_REG, argc, argv - 1, 0, NULL_REG,
- s->_executionStack.size()-1, SCI_XS_CALLEE_LOCALS);
- xstack->selector = kernelFuncNum;
- xstack->type = EXEC_STACK_TYPE_KERNEL;
-
- //warning("callk %s", kernelFunc.orig_name.c_str());
-
- // TODO: SCI2/SCI2.1+ equivalent, once saving/loading works in SCI2/SCI2.1+
- if (g_loadFromLauncher >= 0 && kernelFuncNum == 0x8) {
- // A game is being loaded from the launcher, and kDisplay is called, all initialization has taken
- // place (i.e. menus have been constructed etc). Therefore, inject a kRestoreGame call
- // here, instead of the requested function.
- int saveSlot = g_loadFromLauncher;
- g_loadFromLauncher = -1; // invalidate slot, so that we don't load again
-
- if (saveSlot < 0)
- error("Requested to load invalid save slot"); // should never happen, really
-
- reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL)
- kRestoreGame(s, 2, restoreArgv);
- } else {
- // Call kernel function
- s->r_acc = kernelFunc.fun(s, argc, argv);
+static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
+ Kernel *kernel = g_sci->getKernel();
+
+ if (kernelCallNr >= (int)kernel->_kernelFuncs.size())
+ error("Invalid kernel function 0x%x requested", kernelCallNr);
+
+ const KernelFunction &kernelCall = kernel->_kernelFuncs[kernelCallNr];
+ reg_t *argv = s->xs->sp + 1;
+
+ if (kernelCall.signature
+ && !kernel->signatureMatch(kernelCall.signature, argc, argv)) {
+ // signature mismatch, check if a workaround is available
+ SciTrackOriginReply originReply;
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply);
+ switch (solution.type) {
+ case WORKAROUND_NONE:
+ kernel->signatureDebug(kernelCall.signature, argc, argv);
+ error("[VM] k%s[%x]: signature mismatch via method %s::%s (script %d, room %d, localCall 0x%x)",
+ kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(),
+ originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset);
+ break;
+ case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone
+ return;
+ case WORKAROUND_STILLCALL: // call kernel anyway
+ break;
+ case WORKAROUND_FAKE: // don't do kernel call, fake acc
+ s->r_acc = make_reg(0, solution.value);
+ return;
+ default:
+ error("unknown workaround type");
}
+ }
- // Remove callk stack frame again
- s->_executionStack.pop_back();
+
+ // Call kernel function
+ if (!kernelCall.subFunctionCount) {
+ addKernelCallToExecStack(s, kernelCallNr, argc, argv);
+ s->r_acc = kernelCall.function(s, argc, argv);
+
+ if (kernelCall.debugLogging)
+ logKernelCall(&kernelCall, NULL, s, argc, argv, s->r_acc);
+ if (kernelCall.debugBreakpoint) {
+ printf("Break on k%s\n", kernelCall.name);
+ g_sci->_debugState.debugging = true;
+ g_sci->_debugState.breakpointWasHit = true;
+ }
} else {
- Common::String warningMsg = "Dummy function " + kernelFunc.orig_name +
- Common::String::printf("[0x%x]", kernelFuncNum) +
- " invoked - ignoring. Params: " +
- Common::String::printf("%d", argc) + " (";
-
- for (int i = 0; i < argc; i++) {
- warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
- warningMsg += (i == argc - 1 ? ")" : ", ");
+ // Sub-functions available, check signature and call that one directly
+ if (argc < 1)
+ error("[VM] k%s[%x]: no subfunction-id parameter given", kernelCall.name, kernelCallNr);
+ if (argv[0].segment)
+ error("[VM] k%s[%x]: given subfunction-id is actually a pointer", kernelCall.name, kernelCallNr);
+ const uint16 subId = argv[0].toUint16();
+ // Skip over subfunction-id
+ argc--;
+ argv++;
+ if (subId >= kernelCall.subFunctionCount)
+ error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId);
+ const KernelSubFunction &kernelSubCall = kernelCall.subFunctions[subId];
+ if (kernelSubCall.signature && !kernel->signatureMatch(kernelSubCall.signature, argc, argv)) {
+ // Signature mismatch
+ SciTrackOriginReply originReply;
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply);
+ switch (solution.type) {
+ case WORKAROUND_NONE: {
+ kernel->signatureDebug(kernelSubCall.signature, argc, argv);
+ int callNameLen = strlen(kernelCall.name);
+ if (strncmp(kernelCall.name, kernelSubCall.name, callNameLen) == 0) {
+ const char *subCallName = kernelSubCall.name + callNameLen;
+ error("[VM] k%s(%s): signature mismatch via method %s::%s (script %d, room %d, localCall %x)",
+ kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(),
+ originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset);
+ }
+ error("[VM] k%s: signature mismatch via method %s::%s (script %d, room %d, localCall %x)",
+ kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(),
+ originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset);
+ break;
+ }
+ case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone
+ return;
+ case WORKAROUND_STILLCALL: // call kernel anyway
+ break;
+ case WORKAROUND_FAKE: // don't do kernel call, fake acc
+ s->r_acc = make_reg(0, solution.value);
+ return;
+ default:
+ error("unknown workaround type");
+ }
+ }
+ if (!kernelSubCall.function)
+ error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId);
+ addKernelCallToExecStack(s, kernelCallNr, argc, argv);
+ s->r_acc = kernelSubCall.function(s, argc, argv);
+
+ if (kernelSubCall.debugLogging)
+ logKernelCall(&kernelCall, &kernelSubCall, s, argc, argv, s->r_acc);
+ if (kernelSubCall.debugBreakpoint) {
+ printf("Break on k%s\n", kernelSubCall.name);
+ g_sci->_debugState.debugging = true;
+ g_sci->_debugState.breakpointWasHit = true;
}
-
- warning("%s", warningMsg.c_str());
}
+
+ // Remove callk stack frame again, if there's still an execution stack
+ if (s->_executionStack.begin() != s->_executionStack.end())
+ s->_executionStack.pop_back();
}
-static void gc_countdown(EngineState *s) {
- if (s->gc_countdown-- <= 0) {
- s->gc_countdown = script_gc_interval;
+static void gcCountDown(EngineState *s) {
+ if (s->gcCountDown-- <= 0) {
+ s->gcCountDown = s->scriptGCInterval;
run_gc(s);
}
}
-static const byte _fake_return_buffer[2] = {op_ret << 1, op_ret << 1};
-
-
int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4]) {
uint offset = 0;
extOpcode = src[offset++]; // Get "extended" opcode (lower bit has special meaning)
@@ -696,53 +876,31 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4])
return offset;
}
-void run_vm(EngineState *s, bool restoring) {
+void run_vm(EngineState *s) {
assert(s);
-#ifndef DISABLE_VALIDATIONS
- unsigned int code_buf_size = 0 ; // (Avoid spurious warning)
-#endif
int temp;
- int16 aux_acc; // Auxiliary 16 bit accumulator
reg_t r_temp; // Temporary register
StackPtr s_temp; // Temporary stack pointer
int16 opparams[4]; // opcode parameters
- scriptState.restAdjust = s->restAdjust;
- // &rest adjusts the parameter count by this value
+ s->restAdjust = 0; // &rest adjusts the parameter count by this value
// Current execution data:
- scriptState.xs = &(s->_executionStack.back());
+ s->xs = &(s->_executionStack.back());
ExecStack *xs_new = NULL;
- Object *obj = s->_segMan->getObject(scriptState.xs->objp);
- Script *local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment);
- int old_execution_stack_base = s->execution_stack_base;
+ Object *obj = s->_segMan->getObject(s->xs->objp);
+ Script *scr = 0;
+ Script *local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment);
+ int old_executionStackBase = s->executionStackBase;
// Used to detect the stack bottom, for "physical" returns
- const byte *code_buf = NULL; // (Avoid spurious warning)
- if (!local_script) {
+ if (!local_script)
error("run_vm(): program counter gone astray (local_script pointer is null)");
- }
- if (!restoring)
- s->execution_stack_base = s->_executionStack.size()-1;
+ s->executionStackBase = s->_executionStack.size() - 1;
-#ifndef DISABLE_VALIDATIONS
- // Initialize maximum variable count
- if (s->script_000->_localsBlock)
- scriptState.variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size();
- else
- scriptState.variables_max[VAR_GLOBAL] = 0;
-#endif
-
- scriptState.variables_seg[VAR_GLOBAL] = s->script_000->_localsSegment;
- scriptState.variables_seg[VAR_TEMP] = scriptState.variables_seg[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK);
- scriptState.variables_base[VAR_TEMP] = scriptState.variables_base[VAR_PARAM] = s->stack_base;
-
- // SCI code reads the zeroth argument to determine argc
- if (s->script_000->_localsBlock)
- scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin();
- else
- scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = NULL;
+ s->variablesSegment[VAR_TEMP] = s->variablesSegment[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK);
+ s->variablesBase[VAR_TEMP] = s->variablesBase[VAR_PARAM] = s->stack_base;
s->_executionStackPosChanged = true; // Force initialization
@@ -750,99 +908,89 @@ void run_vm(EngineState *s, bool restoring) {
int var_type; // See description below
int var_number;
- g_debugState.old_pc_offset = scriptState.xs->addr.pc.offset;
- g_debugState.old_sp = scriptState.xs->sp;
+ g_sci->_debugState.old_pc_offset = s->xs->addr.pc.offset;
+ g_sci->_debugState.old_sp = s->xs->sp;
+
+ if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return; // Stop processing
if (s->_executionStackPosChanged) {
- Script *scr;
- scriptState.xs = &(s->_executionStack.back());
+ scr = s->_segMan->getScriptIfLoaded(s->xs->addr.pc.segment);
+ if (!scr)
+ error("No script in segment %d", s->xs->addr.pc.segment);
+ s->xs = &(s->_executionStack.back());
s->_executionStackPosChanged = false;
- scr = s->_segMan->getScriptIfLoaded(scriptState.xs->addr.pc.segment);
- if (!scr) {
- // No script? Implicit return via fake instruction buffer
- warning("Running on non-existant script in segment %x", scriptState.xs->addr.pc.segment);
- code_buf = _fake_return_buffer;
-#ifndef DISABLE_VALIDATIONS
- code_buf_size = 2;
-#endif
- scriptState.xs->addr.pc.offset = 1;
-
- scr = NULL;
- obj = NULL;
+ obj = s->_segMan->getObject(s->xs->objp);
+ local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment);
+ if (!local_script) {
+ error("Could not find local script from segment %x", s->xs->local_segment);
} else {
- obj = s->_segMan->getObject(scriptState.xs->objp);
- code_buf = scr->_buf;
-#ifndef DISABLE_VALIDATIONS
- code_buf_size = scr->_bufSize;
-#endif
- local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment);
- if (!local_script) {
- warning("Could not find local script from segment %x", scriptState.xs->local_segment);
- local_script = NULL;
- scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL;
-#ifndef DISABLE_VALIDATIONS
- scriptState.variables_max[VAR_LOCAL] = 0;
-#endif
- } else {
-
- scriptState.variables_seg[VAR_LOCAL] = local_script->_localsSegment;
- if (local_script->_localsBlock)
- scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin();
- else
- scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL;
-#ifndef DISABLE_VALIDATIONS
- if (local_script->_localsBlock)
- scriptState.variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size();
- else
- scriptState.variables_max[VAR_LOCAL] = 0;
- scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp;
- scriptState.variables_max[VAR_PARAM] = scriptState.xs->argc + 1;
-#endif
- }
- scriptState.variables[VAR_TEMP] = scriptState.xs->fp;
- scriptState.variables[VAR_PARAM] = scriptState.xs->variables_argp;
+ s->variablesSegment[VAR_LOCAL] = local_script->_localsSegment;
+ if (local_script->_localsBlock)
+ s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin();
+ else
+ s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
+ if (local_script->_localsBlock)
+ s->variablesMax[VAR_LOCAL] = local_script->_localsBlock->_locals.size();
+ else
+ s->variablesMax[VAR_LOCAL] = 0;
+ s->variablesMax[VAR_TEMP] = s->xs->sp - s->xs->fp;
+ s->variablesMax[VAR_PARAM] = s->xs->argc + 1;
}
-
+ s->variables[VAR_TEMP] = s->xs->fp;
+ s->variables[VAR_PARAM] = s->xs->variables_argp;
}
- if (script_abort_flag || g_engine->shouldQuit())
- return; // Emergency
+ if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return; // Stop processing
// Debug if this has been requested:
// TODO: re-implement sci_debug_flags
- if (g_debugState.debugging /* sci_debug_flags*/) {
- script_debug(s);
- g_debugState.breakpointWasHit = false;
+ if (g_sci->_debugState.debugging /* sci_debug_flags*/) {
+ g_sci->scriptDebug();
+ g_sci->_debugState.breakpointWasHit = false;
}
Console *con = g_sci->getSciDebugger();
- if (con->isAttached()) {
- con->onFrame();
- }
+ con->onFrame();
-#ifndef DISABLE_VALIDATIONS
- if (scriptState.xs->sp < scriptState.xs->fp)
- error("run_vm(): stack underflow");
+ if (s->xs->sp < s->xs->fp)
+ error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x",
+ PRINT_REG(*s->xs->sp), PRINT_REG(*s->xs->fp));
- scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp;
+ s->variablesMax[VAR_TEMP] = s->xs->sp - s->xs->fp;
- if (scriptState.xs->addr.pc.offset >= code_buf_size)
- error("run_vm(): program counter gone astray");
-#endif
+ if (s->xs->addr.pc.offset >= scr->getBufSize())
+ error("run_vm(): program counter gone astray, addr: %d, code buffer size: %d",
+ s->xs->addr.pc.offset, scr->getBufSize());
// Get opcode
byte extOpcode;
- scriptState.xs->addr.pc.offset += readPMachineInstruction(code_buf + scriptState.xs->addr.pc.offset, extOpcode, opparams);
+ s->xs->addr.pc.offset += readPMachineInstruction(scr->getBuf() + s->xs->addr.pc.offset, extOpcode, opparams);
const byte opcode = extOpcode >> 1;
switch (opcode) {
- case op_bnot: // 0x00 (00)
- s->r_acc = ACC_ARITHMETIC_L(0xffff ^ /*acc*/);
+ case op_bnot: { // 0x00 (00)
+ // Binary not
+ int16 value;
+ if (validate_signedInteger(s->r_acc, value))
+ s->r_acc = make_reg(0, 0xffff ^ value);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG);
break;
+ }
case op_add: // 0x01 (01)
r_temp = POP32();
+
+ // Happens in SQ1, room 28, when throwing the water at Orat
+ if (s->r_acc.segment == 0xFFFF) {
+ // WORKAROUND: init uninitialized variable to 0
+ warning("op_add: attempt to write to uninitialized variable");
+ s->r_acc = NULL_REG;
+ }
+
if (r_temp.segment || s->r_acc.segment) {
reg_t r_ptr = NULL_REG;
int offset;
@@ -896,45 +1044,115 @@ void run_vm(EngineState *s, bool restoring) {
}
break;
- case op_mul: // 0x03 (03)
- s->r_acc = ACC_ARITHMETIC_L(((int16)POP()) * (int16)/*acc*/);
+ case op_mul: { // 0x03 (03)
+ r_temp = POP32();
+ int16 value1, value2;
+ if (validate_signedInteger(s->r_acc, value1) && validate_signedInteger(r_temp, value2))
+ s->r_acc = make_reg(0, value1 * value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeMulWorkarounds, s->r_acc, r_temp);
break;
+ }
- case op_div: // 0x04 (04)
- ACC_AUX_LOAD();
- aux_acc = aux_acc != 0 ? ((int16)POP()) / aux_acc : 0;
- ACC_AUX_STORE();
+ case op_div: { // 0x04 (04)
+ r_temp = POP32();
+ int16 divisor, dividend;
+ if (validate_signedInteger(s->r_acc, divisor) && validate_signedInteger(r_temp, dividend))
+ s->r_acc = make_reg(0, (divisor != 0 ? dividend / divisor : 0));
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDivWorkarounds, s->r_acc, r_temp);
break;
+ }
+
+ case op_mod: { // 0x05 (05)
+ r_temp = POP32();
- case op_mod: // 0x05 (05)
- ACC_AUX_LOAD();
- aux_acc = aux_acc != 0 ? ((int16)POP()) % aux_acc : 0;
- ACC_AUX_STORE();
+ if (getSciVersion() <= SCI_VERSION_0_LATE) {
+ uint16 modulo, value;
+ if (validate_unsignedInteger(s->r_acc, modulo) && validate_unsignedInteger(r_temp, value))
+ s->r_acc = make_reg(0, (modulo != 0 ? value % modulo : 0));
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp);
+ } else {
+ // In Iceman (and perhaps from SCI0 0.000.685 onwards in general),
+ // handling for negative numbers was added. Since Iceman doesn't
+ // seem to have issues with the older code, we exclude it for now
+ // for simplicity's sake and use the new code for SCI01 and newer
+ // games. Fixes the battlecruiser mini game in SQ5 (room 850),
+ // bug #3035755
+ int16 modulo, value, result;
+ if (validate_signedInteger(s->r_acc, modulo) && validate_signedInteger(r_temp, value)) {
+ modulo = ABS(modulo);
+ result = (modulo != 0 ? value % modulo : 0);
+ if (result < 0)
+ result += modulo;
+ s->r_acc = make_reg(0, result);
+ } else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp);
+ }
break;
+ }
- case op_shr: // 0x06 (06)
- s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) >> /*acc*/);
+ case op_shr: { // 0x06 (06)
+ // Shift right logical
+ r_temp = POP32();
+ uint16 value, shiftCount;
+ if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount))
+ s->r_acc = make_reg(0, value >> shiftCount);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
break;
+ }
- case op_shl: // 0x07 (07)
- s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) << /*acc*/);
+ case op_shl: { // 0x07 (07)
+ // Shift left logical
+ r_temp = POP32();
+ uint16 value, shiftCount;
+ if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount))
+ s->r_acc = make_reg(0, value << shiftCount);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
break;
+ }
- case op_xor: // 0x08 (08)
- s->r_acc = ACC_ARITHMETIC_L(POP() ^ /*acc*/);
+ case op_xor: { // 0x08 (08)
+ r_temp = POP32();
+ uint16 value1, value2;
+ if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2))
+ s->r_acc = make_reg(0, value1 ^ value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
break;
+ }
- case op_and: // 0x09 (09)
- s->r_acc = ACC_ARITHMETIC_L(POP() & /*acc*/);
+ case op_and: { // 0x09 (09)
+ r_temp = POP32();
+ uint16 value1, value2;
+ if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2))
+ s->r_acc = make_reg(0, value1 & value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeAndWorkarounds, r_temp, s->r_acc);
break;
+ }
- case op_or: // 0x0a (10)
- s->r_acc = ACC_ARITHMETIC_L(POP() | /*acc*/);
+ case op_or: { // 0x0a (10)
+ r_temp = POP32();
+ uint16 value1, value2;
+ if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2))
+ s->r_acc = make_reg(0, value1 | value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeOrWorkarounds, r_temp, s->r_acc);
break;
+ }
- case op_neg: // 0x0b (11)
- s->r_acc = ACC_ARITHMETIC_L(-/*acc*/);
+ case op_neg: { // 0x0b (11)
+ int16 value;
+ if (validate_signedInteger(s->r_acc, value))
+ s->r_acc = make_reg(0, -value);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG);
break;
+ }
case op_not: // 0x0c (12)
s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment));
@@ -942,6 +1160,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_eq_: // 0x0d (13)
+ // ==
s->r_prev = s->r_acc;
r_temp = POP32();
s->r_acc = make_reg(0, r_temp == s->r_acc);
@@ -949,6 +1168,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_ne_: // 0x0e (14)
+ // !=
s->r_prev = s->r_acc;
r_temp = POP32();
s->r_acc = make_reg(0, r_temp != s->r_acc);
@@ -956,6 +1176,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_gt_: // 0x0f (15)
+ // >
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
@@ -963,44 +1184,80 @@ void run_vm(EngineState *s, bool restoring) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) > (int16)/*acc*/);
+ } else if (r_temp.segment && !s->r_acc.segment) {
+ if (s->r_acc.offset >= 1000)
+ error("[VM] op_gt: comparison between a pointer and number");
+ // Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
+ // Happens in SQ1, room 28, when throwing the water at Orat
+ s->r_acc = make_reg(0, 1);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 > compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_ge_: // 0x10 (16)
+ // >=
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) >= (int16)/*acc*/);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 >= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeGeWorkarounds, r_temp, s->r_acc);
+ }
break;
case op_lt_: // 0x11 (17)
+ // <
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) < (int16)/*acc*/);
+ } else if (r_temp.segment && !s->r_acc.segment) {
+ if (s->r_acc.offset >= 1000)
+ error("[VM] op_lt: comparison between a pointer and number");
+ // Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
+ // Happens in SQ1, room 58, when giving id-card to robot
+ s->r_acc = make_reg(0, 1);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 < compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_le_: // 0x12 (18)
+ // <=
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) <= (int16)/*acc*/);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 <= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeLeWorkarounds, r_temp, s->r_acc);
+ }
break;
case op_ugt_: // 0x13 (19)
+ // > (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
@@ -1012,17 +1269,23 @@ void run_vm(EngineState *s, bool restoring) {
// (Print "foo") // Pointer to a string
// (Print 420 5) // Reference to the fifth message in text resource 420
- // It works because in those games, the maximum resource number is 999,
- // so any parameter value above that threshold must be a pointer.
+ // It works because in those games, the maximum resource number is 999,
+ // so any parameter value above that threshold must be a pointer.
if (r_temp.segment && (s->r_acc == make_reg(0, 1000)))
s->r_acc = make_reg(0, 1);
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) > /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 > compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_uge_: // 0x14 (20)
+ // >= (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
@@ -1031,24 +1294,37 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = make_reg(0, 1);
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) >= /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 >= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_ult_: // 0x15 (21)
+ // < (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
// See above
- if (r_temp.segment && (s->r_acc == make_reg(0, 1000)))
+ // PQ2 japanese compares pointers to 2000 to find out if its a pointer or a resourceid
+ if (r_temp.segment && (s->r_acc == make_reg(0, 1000) || (s->r_acc == make_reg(0, 2000))))
s->r_acc = NULL_REG;
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) < /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 < compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_ule_: // 0x16 (22)
+ // <= (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
@@ -1057,139 +1333,163 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = NULL_REG;
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) <= /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 <= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_bt: // 0x17 (23)
+ // Branch relative if true
if (s->r_acc.offset || s->r_acc.segment)
- scriptState.xs->addr.pc.offset += opparams[0];
+ s->xs->addr.pc.offset += opparams[0];
break;
case op_bnt: // 0x18 (24)
+ // Branch relative if not true
if (!(s->r_acc.offset || s->r_acc.segment))
- scriptState.xs->addr.pc.offset += opparams[0];
+ s->xs->addr.pc.offset += opparams[0];
break;
case op_jmp: // 0x19 (25)
- scriptState.xs->addr.pc.offset += opparams[0];
+ s->xs->addr.pc.offset += opparams[0];
break;
case op_ldi: // 0x1a (26)
+ // Load data immediate
s->r_acc = make_reg(0, opparams[0]);
break;
case op_push: // 0x1b (27)
+ // Push to stack
PUSH32(s->r_acc);
break;
case op_pushi: // 0x1c (28)
+ // Push immediate
PUSH(opparams[0]);
break;
case op_toss: // 0x1d (29)
- scriptState.xs->sp--;
+ // TOS (Top Of Stack) subtract
+ s->xs->sp--;
break;
case op_dup: // 0x1e (30)
- r_temp = scriptState.xs->sp[-1];
+ // Duplicate TOD (Top Of Stack) element
+ r_temp = s->xs->sp[-1];
PUSH32(r_temp);
break;
case op_link: // 0x1f (31)
+ // We shouldn't initialize temp variables at all
+ // We put special segment 0xFFFF in there, so that uninitialized reads can get detected
for (int i = 0; i < opparams[0]; i++)
- scriptState.xs->sp[i] = NULL_REG;
- scriptState.xs->sp += opparams[0];
+ s->xs->sp[i] = make_reg(0xffff, 0);
+
+ s->xs->sp += opparams[0];
break;
case op_call: { // 0x20 (32)
+ // Call a script subroutine
int argc = (opparams[1] >> 1) // Given as offset, but we need count
- + 1 + scriptState.restAdjust;
- StackPtr call_base = scriptState.xs->sp - argc;
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
-
- xs_new = add_exec_stack_entry(s->_executionStack, make_reg(scriptState.xs->addr.pc.segment,
- scriptState.xs->addr.pc.offset + opparams[0]),
- scriptState.xs->sp, scriptState.xs->objp,
- (validate_arithmetic(*call_base)) + scriptState.restAdjust,
- call_base, NULL_SELECTOR, scriptState.xs->objp,
- s->_executionStack.size()-1, scriptState.xs->local_segment);
- scriptState.restAdjust = 0; // Used up the &rest adjustment
- scriptState.xs->sp = call_base;
+ + 1 + s->restAdjust;
+ StackPtr call_base = s->xs->sp - argc;
+ s->xs->sp[1].offset += s->restAdjust;
+
+ uint16 localCallOffset = s->xs->addr.pc.offset + opparams[0];
+
+ xs_new = add_exec_stack_entry(s->_executionStack, make_reg(s->xs->addr.pc.segment,
+ localCallOffset),
+ s->xs->sp, s->xs->objp,
+ (validate_arithmetic(*call_base)) + s->restAdjust,
+ call_base, NULL_SELECTOR, -1, localCallOffset, s->xs->objp,
+ s->_executionStack.size()-1, s->xs->local_segment);
+ s->restAdjust = 0; // Used up the &rest adjustment
+ s->xs->sp = call_base;
s->_executionStackPosChanged = true;
break;
}
case op_callk: { // 0x21 (33)
- gc_countdown(s);
+ // Call kernel function
+ gcCountDown(s);
- scriptState.xs->sp -= (opparams[1] >> 1) + 1;
+ s->xs->sp -= (opparams[1] >> 1) + 1;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- if (!oldScriptHeader) {
- scriptState.xs->sp -= scriptState.restAdjust;
- s->restAdjust = 0; // We just used up the scriptState.restAdjust, remember?
- }
+ if (!oldScriptHeader)
+ s->xs->sp -= s->restAdjust;
- int argc = validate_arithmetic(scriptState.xs->sp[0]);
+ int argc = validate_arithmetic(s->xs->sp[0]);
if (!oldScriptHeader)
- argc += scriptState.restAdjust;
+ argc += s->restAdjust;
callKernelFunc(s, opparams[0], argc);
if (!oldScriptHeader)
- scriptState.restAdjust = s->restAdjust;
+ s->restAdjust = 0;
// Calculate xs again: The kernel function might
// have spawned a new VM
xs_new = &(s->_executionStack.back());
s->_executionStackPosChanged = true;
+
+ // If a game is being loaded, stop processing
+ if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return; // Stop processing
+
break;
}
case op_callb: // 0x22 (34)
- temp = ((opparams[1] >> 1) + scriptState.restAdjust + 1);
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= temp;
-
- scriptState.xs->sp[0].offset += scriptState.restAdjust;
- xs_new = execute_method(s, 0, opparams[0], s_temp, scriptState.xs->objp,
- scriptState.xs->sp[0].offset, scriptState.xs->sp);
- scriptState.restAdjust = 0; // Used up the &rest adjustment
+ // Call base script
+ temp = ((opparams[1] >> 1) + s->restAdjust + 1);
+ s_temp = s->xs->sp;
+ s->xs->sp -= temp;
+
+ s->xs->sp[0].offset += s->restAdjust;
+ xs_new = execute_method(s, 0, opparams[0], s_temp, s->xs->objp,
+ s->xs->sp[0].offset, s->xs->sp);
+ s->restAdjust = 0; // Used up the &rest adjustment
if (xs_new) // in case of error, keep old stack
s->_executionStackPosChanged = true;
break;
case op_calle: // 0x23 (35)
- temp = ((opparams[2] >> 1) + scriptState.restAdjust + 1);
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= temp;
+ // Call external script
+ temp = ((opparams[2] >> 1) + s->restAdjust + 1);
+ s_temp = s->xs->sp;
+ s->xs->sp -= temp;
- scriptState.xs->sp[0].offset += scriptState.restAdjust;
- xs_new = execute_method(s, opparams[0], opparams[1], s_temp, scriptState.xs->objp,
- scriptState.xs->sp[0].offset, scriptState.xs->sp);
- scriptState.restAdjust = 0; // Used up the &rest adjustment
+ s->xs->sp[0].offset += s->restAdjust;
+ xs_new = execute_method(s, opparams[0], opparams[1], s_temp, s->xs->objp,
+ s->xs->sp[0].offset, s->xs->sp);
+ s->restAdjust = 0; // Used up the &rest adjustment
if (xs_new) // in case of error, keep old stack
s->_executionStackPosChanged = true;
break;
case op_ret: // 0x24 (36)
+ // Return from an execution loop started by call, calle, callb, send, self or super
do {
- StackPtr old_sp2 = scriptState.xs->sp;
- StackPtr old_fp = scriptState.xs->fp;
+ StackPtr old_sp2 = s->xs->sp;
+ StackPtr old_fp = s->xs->fp;
ExecStack *old_xs = &(s->_executionStack.back());
- if ((int)s->_executionStack.size()-1 == s->execution_stack_base) { // Have we reached the base?
- s->execution_stack_base = old_execution_stack_base; // Restore stack base
+ if ((int)s->_executionStack.size() - 1 == s->executionStackBase) { // Have we reached the base?
+ s->executionStackBase = old_executionStackBase; // Restore stack base
s->_executionStack.pop_back();
s->_executionStackPosChanged = true;
- s->restAdjust = scriptState.restAdjust; // Update &rest
return; // "Hard" return
}
@@ -1205,33 +1505,34 @@ void run_vm(EngineState *s, bool restoring) {
// Not reached the base, so let's do a soft return
s->_executionStack.pop_back();
s->_executionStackPosChanged = true;
- scriptState.xs = &(s->_executionStack.back());
+ s->xs = &(s->_executionStack.back());
- if (scriptState.xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer
- || scriptState.xs->type != EXEC_STACK_TYPE_CALL) {
- scriptState.xs->sp = old_sp2;
- scriptState.xs->fp = old_fp;
+ if (s->xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer
+ || s->xs->type != EXEC_STACK_TYPE_CALL) {
+ s->xs->sp = old_sp2;
+ s->xs->fp = old_fp;
}
- } while (scriptState.xs->type == EXEC_STACK_TYPE_VARSELECTOR);
+ } while (s->xs->type == EXEC_STACK_TYPE_VARSELECTOR);
// Iterate over all varselector accesses
s->_executionStackPosChanged = true;
- xs_new = scriptState.xs;
+ xs_new = s->xs;
break;
case op_send: // 0x25 (37)
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack
+ // Send for one or more selectors
+ s_temp = s->xs->sp;
+ s->xs->sp -= ((opparams[0] >> 1) + s->restAdjust); // Adjust stack
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
+ s->xs->sp[1].offset += s->restAdjust;
xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp,
- (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust, scriptState.xs->sp);
+ (int)(opparams[0] >> 1) + (uint16)s->restAdjust, s->xs->sp);
- if (xs_new && xs_new != scriptState.xs)
+ if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- scriptState.restAdjust = 0;
+ s->restAdjust = 0;
break;
@@ -1241,8 +1542,9 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_class: // 0x28 (40)
+ // Get class address
s->r_acc = s->_segMan->getClassAddress((unsigned)opparams[0], SCRIPT_GET_LOCK,
- scriptState.xs->addr.pc);
+ s->xs->addr.pc);
break;
case 0x29: // (41)
@@ -1250,58 +1552,62 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_self: // 0x2a (42)
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack
+ // Send to self
+ s_temp = s->xs->sp;
+ s->xs->sp -= ((opparams[0] >> 1) + s->restAdjust); // Adjust stack
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
- xs_new = send_selector(s, scriptState.xs->objp, scriptState.xs->objp,
- s_temp, (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust,
- scriptState.xs->sp);
+ s->xs->sp[1].offset += s->restAdjust;
+ xs_new = send_selector(s, s->xs->objp, s->xs->objp,
+ s_temp, (int)(opparams[0] >> 1) + (uint16)s->restAdjust,
+ s->xs->sp);
- if (xs_new && xs_new != scriptState.xs)
+ if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- scriptState.restAdjust = 0;
+ s->restAdjust = 0;
break;
case op_super: // 0x2b (43)
- r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, scriptState.xs->addr.pc);
+ // Send to any class
+ r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, s->xs->addr.pc);
if (!r_temp.segment)
error("[VM]: Invalid superclass in object");
else {
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= ((opparams[1] >> 1) + scriptState.restAdjust); // Adjust stack
+ s_temp = s->xs->sp;
+ s->xs->sp -= ((opparams[1] >> 1) + s->restAdjust); // Adjust stack
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
- xs_new = send_selector(s, r_temp, scriptState.xs->objp, s_temp,
- (int)(opparams[1] >> 1) + (uint16)scriptState.restAdjust,
- scriptState.xs->sp);
+ s->xs->sp[1].offset += s->restAdjust;
+ xs_new = send_selector(s, r_temp, s->xs->objp, s_temp,
+ (int)(opparams[1] >> 1) + (uint16)s->restAdjust,
+ s->xs->sp);
- if (xs_new && xs_new != scriptState.xs)
+ if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- scriptState.restAdjust = 0;
+ s->restAdjust = 0;
}
break;
case op_rest: // 0x2c (44)
+ // Pushes all or part of the parameter variable list on the stack
temp = (uint16) opparams[0]; // First argument
- scriptState.restAdjust = MAX<int16>(scriptState.xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't
+ s->restAdjust = MAX<int16>(s->xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't
- for (; temp <= scriptState.xs->argc; temp++)
- PUSH32(scriptState.xs->variables_argp[temp]);
+ for (; temp <= s->xs->argc; temp++)
+ PUSH32(s->xs->variables_argp[temp]);
break;
case op_lea: // 0x2d (45)
+ // Load Effective Address
temp = (uint16) opparams[0] >> 1;
var_number = temp & 0x03; // Get variable type
// Get variable block offset
- r_temp.segment = scriptState.variables_seg[var_number];
- r_temp.offset = scriptState.variables[var_number] - scriptState.variables_base[var_number];
+ r_temp.segment = s->variablesSegment[var_number];
+ r_temp.offset = s->variables[var_number] - s->variablesBase[var_number];
if (temp & 0x08) // Add accumulator offset if requested
r_temp.offset += signed_validate_arithmetic(s->r_acc);
@@ -1314,7 +1620,8 @@ void run_vm(EngineState *s, bool restoring) {
case op_selfID: // 0x2e (46)
- s->r_acc = scriptState.xs->objp;
+ // Get 'self' identity
+ s->r_acc = s->xs->objp;
break;
case 0x2f: // (47)
@@ -1322,107 +1629,121 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_pprev: // 0x30 (48)
+ // Pushes the value of the prev register, set by the last comparison
+ // bytecode (eq?, lt?, etc.), on the stack
PUSH32(s->r_prev);
break;
case op_pToa: // 0x31 (49)
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ // Property To Accumulator
+ s->r_acc = validate_property(obj, (opparams[0] >> 1));
break;
case op_aTop: // 0x32 (50)
- OBJ_PROPERTY(obj, (opparams[0] >> 1)) = s->r_acc;
+ // Accumulator To Property
+ validate_property(obj, (opparams[0] >> 1)) = s->r_acc;
break;
case op_pTos: // 0x33 (51)
- PUSH32(OBJ_PROPERTY(obj, opparams[0] >> 1));
+ // Property To Stack
+ PUSH32(validate_property(obj, opparams[0] >> 1));
break;
case op_sTop: // 0x34 (52)
- OBJ_PROPERTY(obj, (opparams[0] >> 1)) = POP32();
+ // Stack To Property
+ validate_property(obj, (opparams[0] >> 1)) = POP32();
break;
- case op_ipToa: // 0x35 (53)
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(1 + /*acc*/);
+ case op_ipToa: { // 0x35 (53)
+ // Increment Property and copy To Accumulator
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ s->r_acc = make_reg(0, valueProperty + 1);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG);
+ opProperty = s->r_acc;
break;
+ }
case op_dpToa: { // 0x36 (54)
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
-#if 0
- // Speed throttling is possible here as well
- // although this opens other issues like mud wrestling in lsl5 uses another local variable for delays
- Object *var_container = obj;
- if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS))
- var_container = s->_segMan->getObject(obj->getSuperClassSelector());
- uint16 varSelector = var_container->getVarSelector(opparams[0] >> 1);
-// printf("%X\n", varSelector);
-// printf("%s\n", g_sci->getKernel()->getSelectorName(varSelector).c_str());
- if ((varSelector == 0x84) || (varSelector == 0x92))) {
- // selectors cycles, cycleCnt from lsl5 hardcoded
- uint32 curTime = g_system->getMillis();
- if (s->_lastAnimateTime + 30 > curTime)
- break;
- s->_lastAnimateTime = curTime;
- }
-#endif
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(-1 + /*acc*/);
+ // Decrement Property and copy To Accumulator
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ s->r_acc = make_reg(0, valueProperty - 1);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDptoaWorkarounds, opProperty, NULL_REG);
+ opProperty = s->r_acc;
break;
}
- case op_ipTos: // 0x37 (55)
- validate_arithmetic(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
- temp = ++OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
- PUSH(temp);
+ case op_ipTos: { // 0x37 (55)
+ // Increment Property and push to Stack
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ valueProperty++;
+ else
+ valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset;
+ opProperty = make_reg(0, valueProperty);
+ PUSH(valueProperty);
break;
+ }
- case op_dpTos: // 0x38 (56)
- validate_arithmetic(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
- temp = --OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
- PUSH(temp);
+ case op_dpTos: { // 0x38 (56)
+ // Decrement Property and push to Stack
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ valueProperty--;
+ else
+ valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset;
+ opProperty = make_reg(0, valueProperty);
+ PUSH(valueProperty);
break;
+ }
case op_lofsa: // 0x39 (57)
- s->r_acc.segment = scriptState.xs->addr.pc.segment;
+ // Load Offset to Accumulator
+ s->r_acc.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
case SCI_VERSION_1_1:
- s->r_acc.offset = opparams[0] + local_script->_scriptSize;
+ s->r_acc.offset = opparams[0] + local_script->getScriptSize();
break;
case SCI_VERSION_1_MIDDLE:
s->r_acc.offset = opparams[0];
break;
default:
- s->r_acc.offset = scriptState.xs->addr.pc.offset + opparams[0];
+ s->r_acc.offset = s->xs->addr.pc.offset + opparams[0];
}
-#ifndef DISABLE_VALIDATIONS
- if (s->r_acc.offset >= code_buf_size) {
+ if (s->r_acc.offset >= scr->getBufSize()) {
error("VM: lofsa operation overflowed: %04x:%04x beyond end"
- " of script (at %04x)\n", PRINT_REG(s->r_acc), code_buf_size);
+ " of script (at %04x)\n", PRINT_REG(s->r_acc), scr->getBufSize());
}
-#endif
break;
case op_lofss: // 0x3a (58)
- r_temp.segment = scriptState.xs->addr.pc.segment;
+ // Load Offset to Stack
+ r_temp.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
case SCI_VERSION_1_1:
- r_temp.offset = opparams[0] + local_script->_scriptSize;
+ r_temp.offset = opparams[0] + local_script->getScriptSize();
break;
case SCI_VERSION_1_MIDDLE:
r_temp.offset = opparams[0];
break;
default:
- r_temp.offset = scriptState.xs->addr.pc.offset + opparams[0];
+ r_temp.offset = s->xs->addr.pc.offset + opparams[0];
}
-#ifndef DISABLE_VALIDATIONS
- if (r_temp.offset >= code_buf_size) {
+ if (r_temp.offset >= scr->getBufSize()) {
error("VM: lofss operation overflowed: %04x:%04x beyond end"
- " of script (at %04x)", PRINT_REG(r_temp), code_buf_size);
+ " of script (at %04x)", PRINT_REG(r_temp), scr->getBufSize());
}
-#endif
PUSH32(r_temp);
break;
@@ -1440,10 +1761,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_pushSelf: // 0x3e (62)
if (!(extOpcode & 1)) {
- PUSH32(scriptState.xs->objp);
+ PUSH32(s->xs->objp);
} else {
// Debug opcode op_file, skip null-terminated string (file name)
- while (code_buf[scriptState.xs->addr.pc.offset++]) ;
+ const byte *code_buf = scr->getBuf();
+ while (code_buf[s->xs->addr.pc.offset++]) ;
}
break;
@@ -1455,42 +1777,53 @@ void run_vm(EngineState *s, bool restoring) {
case op_lal: // 0x41 (65)
case op_lat: // 0x42 (66)
case op_lap: // 0x43 (67)
+ // Load global, local, temp or param variable into the accumulator
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ s->r_acc = READ_VAR(var_type, var_number);
break;
case op_lsg: // 0x44 (68)
case op_lsl: // 0x45 (69)
case op_lst: // 0x46 (70)
case op_lsp: // 0x47 (71)
+ // Load global, local, temp or param variable into the stack
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ PUSH32(READ_VAR(var_type, var_number));
break;
case op_lagi: // 0x48 (72)
case op_lali: // 0x49 (73)
case op_lati: // 0x4a (74)
case op_lapi: // 0x4b (75)
+ // Load global, local, temp or param variable into the accumulator,
+ // using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ s->r_acc = READ_VAR(var_type, var_number);
break;
case op_lsgi: // 0x4c (76)
case op_lsli: // 0x4d (77)
case op_lsti: // 0x4e (78)
- case op_lspi: // 0x4f (79)
+ case op_lspi: { // 0x4f (79)
+ // Load global, local, temp or param variable into the stack,
+ // using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
- var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ int16 value;
+ if (!validate_signedInteger(s->r_acc, value))
+ value = arithmetic_lookForWorkaround(opcode, opcodeLsiWorkarounds, s->r_acc, NULL_REG).offset;
+ var_number = opparams[0] + value;
+ PUSH32(READ_VAR(var_type, var_number));
break;
+ }
case op_sag: // 0x50 (80)
case op_sal: // 0x51 (81)
case op_sat: // 0x52 (82)
case op_sap: // 0x53 (83)
+ // Save the accumulator into the global, local, temp or param variable
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
WRITE_VAR(var_type, var_number, s->r_acc);
@@ -1500,6 +1833,7 @@ void run_vm(EngineState *s, bool restoring) {
case op_ssl: // 0x55 (85)
case op_sst: // 0x56 (86)
case op_ssp: // 0x57 (87)
+ // Save the stack into the global, local, temp or param variable
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
WRITE_VAR(var_type, var_number, POP32());
@@ -1509,6 +1843,9 @@ void run_vm(EngineState *s, bool restoring) {
case op_sali: // 0x59 (89)
case op_sati: // 0x5a (90)
case op_sapi: // 0x5b (91)
+ // Save the accumulator into the global, local, temp or param variable,
+ // using the accumulator as an additional index
+
// Special semantics because it wouldn't really make a whole lot
// of sense otherwise, with acc being used for two things
// simultaneously...
@@ -1522,6 +1859,8 @@ void run_vm(EngineState *s, bool restoring) {
case op_ssli: // 0x5d (93)
case op_ssti: // 0x5e (94)
case op_sspi: // 0x5f (95)
+ // Save the stack into the global, local, temp or param variable,
+ // using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
WRITE_VAR(var_type, var_number, POP32());
@@ -1531,9 +1870,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plusal: // 0x61 (97)
case op_plusat: // 0x62 (98)
case op_plusap: // 0x63 (99)
+ // Increment the global, local, temp or param variable and save it
+ // to the accumulator
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, 1);
@@ -1546,9 +1887,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plussl: // 0x65 (101)
case op_plusst: // 0x66 (102)
case op_plussp: // 0x67 (103)
+ // Increment the global, local, temp or param variable and save it
+ // to the stack
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, 1);
@@ -1562,9 +1905,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plusali: // 0x69 (105)
case op_plusati: // 0x6a (106)
case op_plusapi: // 0x6b (107)
+ // Increment the global, local, temp or param variable and save it
+ // to the accumulator, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, 1);
@@ -1577,9 +1922,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plussli: // 0x6d (109)
case op_plussti: // 0x6e (110)
case op_plusspi: // 0x6f (111)
+ // Increment the global, local, temp or param variable and save it
+ // to the stack, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, 1);
@@ -1593,9 +1940,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minusal: // 0x71 (113)
case op_minusat: // 0x72 (114)
case op_minusap: // 0x73 (115)
+ // Decrement the global, local, temp or param variable and save it
+ // to the accumulator
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, -1);
@@ -1608,9 +1957,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minussl: // 0x75 (117)
case op_minusst: // 0x76 (118)
case op_minussp: // 0x77 (119)
+ // Decrement the global, local, temp or param variable and save it
+ // to the stack
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, -1);
@@ -1624,9 +1975,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minusali: // 0x79 (121)
case op_minusati: // 0x7a (122)
case op_minusapi: // 0x7b (123)
+ // Decrement the global, local, temp or param variable and save it
+ // to the accumulator, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, -1);
@@ -1639,9 +1992,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minussli: // 0x7d (125)
case op_minussti: // 0x7e (126)
case op_minusspi: // 0x7f (127)
+ // Decrement the global, local, temp or param variable and save it
+ // to the stack, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, -1);
@@ -1657,121 +2012,23 @@ void run_vm(EngineState *s, bool restoring) {
} // switch (opcode)
if (s->_executionStackPosChanged) // Force initialization
- scriptState.xs = xs_new;
+ s->xs = xs_new;
-//#ifndef DISABLE_VALIDATIONS
- if (scriptState.xs != &(s->_executionStack.back())) {
- warning("xs is stale (%p vs %p); last command was %02x",
- (void *)scriptState.xs, (void *)&(s->_executionStack.back()),
+ if (s->xs != &(s->_executionStack.back())) {
+ error("xs is stale (%p vs %p); last command was %02x",
+ (void *)s->xs, (void *)&(s->_executionStack.back()),
opcode);
}
-//#endif
- ++script_step_counter;
+ ++s->scriptStepCounter;
}
}
-static void _init_stack_base_with_selector(EngineState *s, Selector selector) {
- s->stack_base[0] = make_reg(0, (uint16)selector);
- s->stack_base[1] = NULL_REG;
-}
-
-static EngineState *_game_run(EngineState *&s) {
- EngineState *successor = NULL;
- int game_is_finished = 0;
-
- if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup))
- g_sci->getSciDebugger()->attach();
-
- do {
- s->_executionStackPosChanged = false;
- run_vm(s, successor ? true : false);
- if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { // Restart was requested?
- successor = NULL;
- s->_executionStack.clear();
- s->_executionStackPosChanged = false;
-
- game_exit(s);
- script_init_engine(s);
- game_init(s);
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- s->_sound.sfx_reset_player();
-#endif
- _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.play);
-
- send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base);
-
- script_abort_flag = 0;
- s->restarting_flags = SCI_GAME_WAS_RESTARTED | SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
-
- } else {
- successor = s->successor;
- if (successor) {
- game_exit(s);
- delete s;
- s = successor;
-
- if (script_abort_flag == 2) {
- debugC(2, kDebugLevelVM, "Restarting with replay()");
- s->_executionStack.clear(); // Restart with replay
-
- _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.replay);
-
- send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base);
- }
-
- script_abort_flag = 0;
-
- } else
- game_is_finished = 1;
- }
- } while (!game_is_finished);
-
- return s;
-}
-
-int game_run(EngineState **_s) {
- EngineState *s = *_s;
-
- debugC(2, kDebugLevelVM, "Calling %s::play()", s->_gameId.c_str());
- _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.play); // Call the play selector
-
- // Now: Register the first element on the execution stack-
- if (!send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base)) {
- Console *con = g_sci->getSciDebugger();
- con->printObject(s->_gameObj);
- warning("Failed to run the game! Aborting...");
- return 1;
- }
- // and ENGAGE!
- _game_run(*_s);
-
- debugC(2, kDebugLevelVM, "Game::play() finished.");
-
- return 0;
-}
-
-void quit_vm() {
- script_abort_flag = 1; // Terminate VM
- g_debugState.seeking = kDebugSeekNothing;
- g_debugState.runningStep = 0;
-}
-
-void shrink_execution_stack(EngineState *s, uint size) {
- assert(s->_executionStack.size() >= size);
- Common::List<ExecStack>::iterator iter;
- iter = s->_executionStack.begin();
- for (uint i = 0; i < size; ++i)
- ++iter;
- s->_executionStack.erase(iter, s->_executionStack.end());
-}
-
-reg_t* ObjVarRef::getPointer(SegManager *segMan) const {
+reg_t *ObjVarRef::getPointer(SegManager *segMan) const {
Object *o = segMan->getObject(obj);
- if (!o) return 0;
- return &(o->_variables[varindex]);
+ return o ? &o->getVariableRef(varindex) : 0;
}
-reg_t* ExecStack::getVarPointer(SegManager *segMan) const {
+reg_t *ExecStack::getVarPointer(SegManager *segMan) const {
assert(type == EXEC_STACK_TYPE_VARSELECTOR);
return addr.varp.getPointer(segMan);
}
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 8e40fed818..ee22e03310 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -43,59 +43,16 @@ class ResourceManager;
/** Number of bytes to be allocated for the stack */
#define VM_STACK_SIZE 0x1000
-/** Maximum number of calls residing on the stack */
-#define SCRIPT_MAX_EXEC_STACK 256
-/** Maximum number of entries in the class table */
-#define SCRIPT_MAX_CLASSTABLE_SIZE 256
-/** Maximum number of cloned objects on the heap */
-#define SCRIPT_MAX_CLONES 256
-
-
-/** Object-relative offset of the selector area inside a script */
-#define SCRIPT_SELECTOR_OFFSET 8 -8
-
-/** Object-relative offset of the pointer to the underlying script's local variables */
-#define SCRIPT_LOCALVARPTR_OFFSET 2 -8
-
-/** Object-relative offset of the selector counter */
-#define SCRIPT_SELECTORCTR_OFFSET 6 -8
-
-/** Object-relative offset of the offset of the function area */
-#define SCRIPT_FUNCTAREAPTR_OFFSET 4 -8
-
-/** Offset that has to be added to the function area pointer */
-#define SCRIPT_FUNCTAREAPTR_MAGIC 8 -8
-
-/** Offset of the name pointer */
-#define SCRIPT_NAME_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 14 -8 : 16)
-
-/** Object-relative offset of the -info- selector */
-#define SCRIPT_INFO_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 12 -8 : 14)
-
-/** Flag fo the -info- selector */
-#define SCRIPT_INFO_CLONE 0x0001
-
-/** Flag for the -info- selector */
-#define SCRIPT_INFO_CLASS 0x8000
-
-
/** Magical object identifier */
#define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234
+
/** Offset of this identifier */
#define SCRIPT_OBJECT_MAGIC_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? -8 : 0)
-/** Script-relative offset of the species ID */
-#define SCRIPT_SPECIES_OFFSET 8 -8
-
-#define SCRIPT_SUPERCLASS_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 10 -8 : 12)
-
-/** Magic adjustment value for lofsa and lofss */
-#define SCRIPT_LOFS_MAGIC 3
-
/** Stack pointer value: Use predecessor's value */
#define CALL_SP_CARRY NULL
-/** Types of selectors as returned by lookup_selector() below. */
+/** Types of selectors as returned by lookupSelector() below. */
enum SelectorType {
kSelectorNone = 0,
kSelectorVariable,
@@ -107,113 +64,7 @@ struct Class {
reg_t reg; ///< offset; script-relative offset, segment: 0 if not instantiated
};
-#define RAW_IS_OBJECT(datablock) (READ_SCI11ENDIAN_UINT16(((byte *) datablock) + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER)
-
-/** Contains selector IDs for a few selected selectors */
-struct SelectorCache {
- SelectorCache() {
- memset(this, 0, sizeof(*this));
- }
-
- // Statically defined selectors, (almost the) same in all SCI versions
- Selector y;
- Selector x;
- Selector view, loop, cel; ///< Description of a specific image
- Selector underBits; ///< Used by the graphics subroutines to store backupped BG pic data
- Selector nsTop, nsLeft, nsBottom, nsRight; ///< View boundaries ('now seen')
- Selector lsTop, lsLeft, lsBottom, lsRight; ///< Used by Animate() subfunctions and scroll list controls
- Selector signal; ///< Used by Animate() to control a view's behaviour
- Selector illegalBits; ///< Used by CanBeHere
- Selector brTop, brLeft, brBottom, brRight; ///< Bounding Rectangle
- // name, key, time
- Selector text; ///< Used by controls
- Selector elements; ///< Used by SetSynonyms()
- // color, back
- Selector mode; ///< Used by text controls (-> DrawControl())
- // style
- Selector state, font, type;///< Used by controls
- // window
- Selector cursor, max; ///< Used by EditControl
- // mark, who
- Selector message; ///< Used by GetEvent
- // edit
- Selector play; ///< Play function (first function to be called)
- Selector number;
- Selector handle; ///< Replaced by nodePtr in SCI1+
- Selector nodePtr; ///< Replaces handle in SCI1+
- Selector client; ///< The object that wants to be moved
- Selector dx, dy; ///< Deltas
- Selector b_movCnt, b_i1, b_i2, b_di, b_xAxis, b_incr; ///< Various Bresenham vars
- Selector xStep, yStep; ///< BR adjustments
- Selector moveSpeed; ///< Used for DoBresen
- Selector canBeHere; ///< Funcselector: Checks for movement validity in SCI0
- Selector heading, mover; ///< Used in DoAvoider
- Selector doit; ///< Called (!) by the Animate() system call
- Selector isBlocked, looper; ///< Used in DoAvoider
- Selector priority;
- Selector modifiers; ///< Used by GetEvent
- Selector replay; ///< Replay function
- // setPri, at, next, done, width
- Selector wordFail, syntaxFail; ///< Used by Parse()
- // semanticFail, pragmaFail
- // said
- Selector claimed; ///< Used generally by the event mechanism
- // value, save, restore, title, button, icon, draw
- Selector delete_; ///< Called by Animate() to dispose a view object
- Selector z;
-
- // SCI1+ static selectors
- Selector parseLang;
- Selector printLang; ///< Used for i18n
- Selector subtitleLang;
- Selector size;
- Selector points; ///< Used by AvoidPath()
- Selector palette;
- Selector dataInc;
- // handle (in SCI1)
- Selector min; ///< SMPTE time format
- Selector sec;
- Selector frame;
- Selector vol;
- Selector pri;
- // perform
- Selector moveDone; ///< used for DoBresen
-
- // SCI1 selectors which have been moved a bit in SCI1.1, but otherwise static
- Selector cantBeHere; ///< Checks for movement avoidance in SCI1+. Replaces canBeHere
- Selector topString; ///< SCI1 scroll lists use this instead of lsTop
- Selector flags;
-
- // SCI1+ audio sync related selectors, not static. They're used for lip syncing in
- // CD talkie games
- Selector syncCue; ///< Used by DoSync()
- Selector syncTime;
-
- // SCI1.1 specific selectors
- Selector scaleSignal; // < Used by Animate() for cel scaling (SCI1.1+)
- Selector scaleX, scaleY; ///< SCI1.1 view scaling
-
- // Used for auto detection purposes
- Selector overlay; ///< Used to determine if a game is using old gfx functions or not
- Selector setCursor; ///< For cursor semantics autodetection
-
-#ifdef ENABLE_SCI32
- Selector data; // Used by Array()/String()
- Selector picture; // Used to hold the picture ID for SCI32 pictures
-
- Selector plane;
- Selector top;
- Selector left;
- Selector bottom;
- Selector right;
- Selector resX;
- Selector resY;
-
- Selector fore;
- Selector back;
- Selector dimmed;
-#endif
-};
+#define RAW_IS_OBJECT(datablock) (READ_SCI11ENDIAN_UINT16(((const byte *) datablock) + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER)
// A reference to an object's variable.
// The object is stored as a reg_t, the variable as an index into _variables
@@ -246,8 +97,10 @@ struct ExecStack {
StackPtr variables_argp; // Argument pointer
SegmentId local_segment; // local variables etc
- Selector selector; // The selector which was used to call or -1 if not applicable
- int origin; // The stack frame position the call was made from, or -1 if it was the initial call
+ Selector debugSelector; // The selector which was used to call or -1 if not applicable
+ int debugExportId; // The exportId which was called or -1 if not applicable
+ int debugLocalCallOffset; // Local call offset or -1 if not applicable
+ int debugOrigin; // The stack frame position the call was made from, or -1 if it was the initial call
ExecStackType type;
reg_t* getVarPointer(SegManager *segMan) const;
@@ -260,41 +113,165 @@ enum {
VAR_PARAM = 3
};
-/**
- * Structure for storing the current internal state of the VM.
- */
-struct ScriptState {
- ExecStack *xs;
- int16 restAdjust;
- reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers
- reg_t *variables_base[4]; ///< Used for referencing VM ops
- SegmentId variables_seg[4]; ///< Same as above, contains segment IDs
- int variables_max[4]; ///< Max. values for all variables
-};
-
-/**
- * The current internal state of the VM.
- */
-extern ScriptState scriptState;
-
-/**
- * Set this to 1 to abort script execution immediately. Aborting will
- * leave the debug exec stack intact.
- * Set it to 2 to force a replay afterwards.
- */
-extern int script_abort_flag;
-
/** Number of kernel calls in between gcs; should be < 50000 */
enum {
GC_INTERVAL = 32768
};
-/** Initially GC_DELAY, can be set at runtime */
-extern int script_gc_interval;
+// Opcode formats
+enum opcode_format {
+ Script_Invalid = -1,
+ Script_None = 0,
+ Script_Byte,
+ Script_SByte,
+ Script_Word,
+ Script_SWord,
+ Script_Variable,
+ Script_SVariable,
+ Script_SRelative,
+ Script_Property,
+ Script_Global,
+ Script_Local,
+ Script_Temp,
+ Script_Param,
+ Script_Offset,
+ Script_End
+};
+
+enum sci_opcodes {
+ op_bnot = 0x00, // 000
+ op_add = 0x01, // 001
+ op_sub = 0x02, // 002
+ op_mul = 0x03, // 003
+ op_div = 0x04, // 004
+ op_mod = 0x05, // 005
+ op_shr = 0x06, // 006
+ op_shl = 0x07, // 007
+ op_xor = 0x08, // 008
+ op_and = 0x09, // 009
+ op_or = 0x0a, // 010
+ op_neg = 0x0b, // 011
+ op_not = 0x0c, // 012
+ op_eq_ = 0x0d, // 013
+ op_ne_ = 0x0e, // 014
+ op_gt_ = 0x0f, // 015
+ op_ge_ = 0x10, // 016
+ op_lt_ = 0x11, // 017
+ op_le_ = 0x12, // 018
+ op_ugt_ = 0x13, // 019
+ op_uge_ = 0x14, // 020
+ op_ult_ = 0x15, // 021
+ op_ule_ = 0x16, // 022
+ op_bt = 0x17, // 023
+ op_bnt = 0x18, // 024
+ op_jmp = 0x19, // 025
+ op_ldi = 0x1a, // 026
+ op_push = 0x1b, // 027
+ op_pushi = 0x1c, // 028
+ op_toss = 0x1d, // 029
+ op_dup = 0x1e, // 030
+ op_link = 0x1f, // 031
+ op_call = 0x20, // 032
+ op_callk = 0x21, // 033
+ op_callb = 0x22, // 034
+ op_calle = 0x23, // 035
+ op_ret = 0x24, // 036
+ op_send = 0x25, // 037
+ // dummy 0x26, // 038
+ // dummy 0x27, // 039
+ op_class = 0x28, // 040
+ // dummy 0x29, // 041
+ op_self = 0x2a, // 042
+ op_super = 0x2b, // 043
+ op_rest = 0x2c, // 044
+ op_lea = 0x2d, // 045
+ op_selfID = 0x2e, // 046
+ // dummy 0x2f // 047
+ op_pprev = 0x30, // 048
+ op_pToa = 0x31, // 049
+ op_aTop = 0x32, // 050
+ op_pTos = 0x33, // 051
+ op_sTop = 0x34, // 052
+ op_ipToa = 0x35, // 053
+ op_dpToa = 0x36, // 054
+ op_ipTos = 0x37, // 055
+ op_dpTos = 0x38, // 056
+ op_lofsa = 0x39, // 057
+ op_lofss = 0x3a, // 058
+ op_push0 = 0x3b, // 059
+ op_push1 = 0x3c, // 060
+ op_push2 = 0x3d, // 061
+ op_pushSelf = 0x3e, // 062
+ op_line = 0x3f, // 063
+ op_lag = 0x40, // 064
+ op_lal = 0x41, // 065
+ op_lat = 0x42, // 066
+ op_lap = 0x43, // 067
+ op_lsg = 0x44, // 068
+ op_lsl = 0x45, // 069
+ op_lst = 0x46, // 070
+ op_lsp = 0x47, // 071
+ op_lagi = 0x48, // 072
+ op_lali = 0x49, // 073
+ op_lati = 0x4a, // 074
+ op_lapi = 0x4b, // 075
+ op_lsgi = 0x4c, // 076
+ op_lsli = 0x4d, // 077
+ op_lsti = 0x4e, // 078
+ op_lspi = 0x4f, // 079
+ op_sag = 0x50, // 080
+ op_sal = 0x51, // 081
+ op_sat = 0x52, // 082
+ op_sap = 0x53, // 083
+ op_ssg = 0x54, // 084
+ op_ssl = 0x55, // 085
+ op_sst = 0x56, // 086
+ op_ssp = 0x57, // 087
+ op_sagi = 0x58, // 088
+ op_sali = 0x59, // 089
+ op_sati = 0x5a, // 090
+ op_sapi = 0x5b, // 091
+ op_ssgi = 0x5c, // 092
+ op_ssli = 0x5d, // 093
+ op_ssti = 0x5e, // 094
+ op_sspi = 0x5f, // 095
+ op_plusag = 0x60, // 096
+ op_plusal = 0x61, // 097
+ op_plusat = 0x62, // 098
+ op_plusap = 0x63, // 099
+ op_plussg = 0x64, // 100
+ op_plussl = 0x65, // 101
+ op_plusst = 0x66, // 102
+ op_plussp = 0x67, // 103
+ op_plusagi = 0x68, // 104
+ op_plusali = 0x69, // 105
+ op_plusati = 0x6a, // 106
+ op_plusapi = 0x6b, // 107
+ op_plussgi = 0x6c, // 108
+ op_plussli = 0x6d, // 109
+ op_plussti = 0x6e, // 110
+ op_plusspi = 0x6f, // 111
+ op_minusag = 0x70, // 112
+ op_minusal = 0x71, // 113
+ op_minusat = 0x72, // 114
+ op_minusap = 0x73, // 115
+ op_minussg = 0x74, // 116
+ op_minussl = 0x75, // 117
+ op_minusst = 0x76, // 118
+ op_minussp = 0x77, // 119
+ op_minusagi = 0x78, // 120
+ op_minusali = 0x79, // 121
+ op_minusati = 0x7a, // 122
+ op_minusapi = 0x7b, // 123
+ op_minussgi = 0x7c, // 124
+ op_minussli = 0x7d, // 125
+ op_minussti = 0x7e, // 126
+ op_minusspi = 0x7f // 127
+};
-/** Number of steps executed */
-extern int script_step_counter;
+extern opcode_format g_opcode_formats[128][4];
+void script_adjust_opcode_formats();
/**
* Executes function pubfunct of the specified script.
@@ -338,9 +315,8 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj,
* It executes the code on s->heap[pc] until it hits a 'ret' operation
* while (stack_base == stack_pos). Requires s to be set up correctly.
* @param[in] s The state to use
- * @param[in] restoring true if s has just been restored, false otherwise
*/
-void run_vm(EngineState *s, bool restoring);
+void run_vm(EngineState *s);
/**
* Debugger functionality
@@ -349,14 +325,6 @@ void run_vm(EngineState *s, bool restoring);
void script_debug(EngineState *s);
/**
- * Initializes a EngineState block
- * @param[in] s The state to initialize
- * @return 0 on success, 1 if vocab.996 (the class table) is missing
- * or corrupted
- */
-int script_init_engine(EngineState *);
-
-/**
* Looks up a selector and returns its type and value
* varindex is written to iff it is non-NULL and the selector indicates a property of the object.
* @param[in] segMan The Segment Manager
@@ -376,105 +344,10 @@ int script_init_engine(EngineState *);
* kSelectorMethod if the selector represents a
* method
*/
-SelectorType lookup_selector(SegManager *segMan, reg_t obj, Selector selectorid,
+SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid,
ObjVarRef *varp, reg_t *fptr);
/**
- * Makes sure that a script and its superclasses get loaded to the heap.
- * If the script already has been loaded, only the number of lockers is
- * increased. All scripts containing superclasses of this script are loaded
- * recursively as well, unless 'recursive' is set to zero. The
- * complementary function is "script_uninstantiate()" below.
- * @param[in] resMan The resource manager
- * @param[in] segMan The segment manager
- * @param[in] script_nr The script number to load
- * @return The script's segment ID or 0 if out of heap
- */
-int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr);
-
-/**
- * Decreases the numer of lockers of a script and unloads it if that number
- * reaches zero.
- * This function will recursively unload scripts containing its
- * superclasses, if those aren't locked by other scripts as well.
- * @param[in] segMan The segment manager
- * @param[in] version The SCI version to use
- * @param[in] script_nr The script number that is requestet to be unloaded
- */
-void script_uninstantiate(SegManager *segMan, int script_nr);
-
-/**
- * Converts the builtin Sierra game IDs to the ones we use in ScummVM
- * @param[in] gameId The internal game ID
- * @param[in] gameFlags The game's flags, which are adjusted accordingly for demos
- * @return The equivalent ScummVM game id
- */
-Common::String convertSierraGameId(const char *gameId, uint32 *gameFlags, ResourceManager *resMan);
-
-/**
- * Initializes an SCI game
- * This function must be run before script_run() is executed. Graphics data
- * is initialized iff s->gfx_state != NULL.
- * @param[in] s The state to operate on
- * @return 0 on success, 1 if an error occured.
- */
-int game_init(EngineState *s);
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-/**
- * Initializes the sound part of an SCI game
- * This function may only be called if game_init() did not initialize
- * the sound data.
- * @param[in] s The state to initialize the sound in
- * @param[in] sound_flags Flags to pass to the sound subsystem
- * @param[in] soundVersion sound-version that got detected during game init
- * @return 0 on success, 1 if an error occured
- */
-int game_init_sound(EngineState *s, int sound_flags, SciVersion soundVersion);
-#endif
-
-/**
- * Runs an SCI game
- * This is the main function for SCI games. It takes a valid state, loads
- * script 0 to it, finds the game object, allocates a stack, and runs the
- * init method of the game object. In layman's terms, this runs an SCI game.
- * Note that, EngineState *s may be changed during the game, e.g. if a game
- * state is restored.
- * @param[in] s Pointer to the pointer of the state to operate on
- * @return 0 on success, 1 if an error occured.
- */
-int game_run(EngineState **s);
-
-/**
- * Restores an SCI game state and runs the game
- * This restores a savegame; otherwise, it behaves just like game_run().
- * @param[in] s Pointer to the pointer of the state to
- * operate on
- * @param[in] savegame_name Name of the savegame to restore
- * @return 0 on success, 1 if an error occured.
- */
-int game_restore(EngineState **s, char *savegame_name);
-
-/**
- * Uninitializes an initialized SCI game
- * This function should be run after each script_run() call.
- * @param[in] s The state to operate on
- * @return 0 on success, 1 if an error occured.
- */
-int game_exit(EngineState *s);
-
-/**
- * Instructs the virtual machine to abort
- */
-void quit_vm();
-
-/**
- * Shrink execution stack to size.
- * Contains an assert it is not already smaller.
- */
-void shrink_execution_stack(EngineState *s, uint size);
-
-/**
* Read a PMachine instruction from a memory buffer and return its length.
*
* @param[in] src address from which to start parsing
diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h
index 29dc798c35..edf35a122a 100644
--- a/engines/sci/engine/vm_types.h
+++ b/engines/sci/engine/vm_types.h
@@ -81,6 +81,7 @@ enum {
extern const reg_t NULL_REG;
extern const reg_t SIGNAL_REG;
+extern const reg_t TRUE_REG;
// Selector ID
typedef int Selector;
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
new file mode 100644
index 0000000000..4dc4ae93d8
--- /dev/null
+++ b/engines/sci/engine/workarounds.cpp
@@ -0,0 +1,457 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "sci/engine/kernel.h"
+#include "sci/engine/state.h"
+#include "sci/engine/vm.h"
+#include "sci/engine/workarounds.h"
+
+#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, -1, 0, { WORKAROUND_NONE, 0 } }
+
+namespace Sci {
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeDivWorkarounds[] = {
+ { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // when entering inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call, index, workaround
+const SciWorkaroundEntry opcodeDptoaWorkarounds[] = {
+ { GID_LSL6, 360, 938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease
+ { GID_LSL6HIRES, 360,64938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease
+ { GID_SQ5, 200, 939, 0, "Osc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when going back to bridge the crew is goofing off, we get an object as cycle count
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeGeWorkarounds[] = {
+ { GID_PQ3, 31, 31, 0, "rm031", "init", -1, 0, { WORKAROUND_FAKE, 1 } }, // pq3 english: when exiting the car, while morales is making phonecalls - bug #3037565
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeLeWorkarounds[] = {
+ { GID_PEPPER, 370, 23, 0, "eastExitFeature", "onMe", -1, 0, { WORKAROUND_FAKE, 1 } }, // Pugh's office, when trying to use either the left or right exits, gets called on an integer and a pointer - bug #3040142
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeLsiWorkarounds[] = {
+ { GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // when getting asked for your name by the astrologer bug #3039879
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeMulWorkarounds[] = {
+ { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #3038913
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeAndWorkarounds[] = {
+ { GID_MOTHERGOOSE, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // constantly during the game
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeOrWorkarounds[] = {
+ { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
+ { GID_CASTLEBRAIN, 280, 280, 0, "programmer", "dispatchEvent", -1, 0, { WORKAROUND_FAKE, 0xf } }, // pressing 'q' on the computer screen in the robot room, and closing the help dialog that pops up (bug #3039656). Moves the cursor to the view with the ID returned (in this case, the robot hand)
+ { GID_CNICK_KQ, 200, 0, 1, "Character", "<noname446>", -1, 504, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3
+ { GID_CNICK_KQ, 200, 0, 1, "Character", "<noname446>", -1, 505, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3
+ { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "<noname183>", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu, like in hoyle 3
+ { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "<noname110>", -1, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game
+ { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms
+ { GID_FANMADE, 516, 979, 0, "", "export 0", -1, 20, { WORKAROUND_FAKE, 0 } }, // Happens in Grotesteing after the logos
+ { GID_FANMADE, 528, 990, 0, "GDialog", "doit", -1, 4, { WORKAROUND_FAKE, 0 } }, // Happens in Cascade Quest when closing the glossary - bug #3038757
+ { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_GK1, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking
+ { GID_GK2, -1, 11, 0, "", "export 10", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts
+ { GID_GK2, -1, 11, 0, "", "export 10", -1, 4, { WORKAROUND_FAKE, 0 } }, // called during the game
+ { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts
+ { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", -1, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game
+ { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, 504, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something
+ { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, 505, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something
+ { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu
+ { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", -1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #3036918
+ { GID_HOYLE4, -1, 0, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #3039294
+ { GID_HOYLE4, 910, 910, 0, "IconBarList", "setup", -1, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #3039294
+ { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", -1, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0
+ { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon
+ { GID_JONES, 1, 232, 0, "weekendText", "draw", 0x3d3, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game
+ { GID_JONES, 1, 255, 0, "", "export 0", -1, 13, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends
+ { GID_JONES, 1, 255, 0, "", "export 0", -1, 14, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends
+ { GID_JONES, 764, 255, 0, "", "export 0", -1, 13, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts
+ { GID_JONES, 764, 255, 0, "", "export 0", -1, 14, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts
+ { GID_KQ5, -1, 0, 0, "", "export 29", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #3034700
+ { GID_KQ5, 25, 25, 0, "rm025", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is
+ { GID_KQ6, -1, 30, 0, "rats", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #3034597, #3035495, #3035824
+ { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", -1, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #3034565
+ { GID_KQ6, 500, 500, 0, "rm500", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast
+ { GID_KQ6, 520, 520, 0, "rm520", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle
+ { GID_KQ6, -1, 903, 0, "controlWin", "open", -1, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc)
+ { GID_KQ7, 30, 64996, 0, "User", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key
+ { GID_LAURABOW, 37, 0, 0, "CB1", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs (bug #3037694)
+ { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", -1, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up (initial bug #3034985)
+ { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", -1, 1, { WORKAROUND_FAKE, 0x7fff } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy). Must be big, as the game scripts perform lt on it and start deleting journal entries - bugs #3035068, #3036274
+ { GID_LAURABOW2, -1, 90, 0, "aTut", "init", -1, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum (bug #3041257)
+ { GID_LAURABOW2, -1, 90, 0, "aZiggy", "init", -1, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum (bug #3041257)
+ { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #3036291
+ { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When giving an answer using the druid hand sign code in any room
+ { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", 0xa8, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #3036601
+ { GID_LSL1, 250, 250, 0, "increase", "handleEvent", -1, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet
+ { GID_LSL1, 720, 720, 0, "rm720", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // age check room
+ { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", -1, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #3036483
+ { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203)
+ { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's')
+ { GID_LSL6, 820, 82, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // when touching the electric fence - bug #3038326
+ { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory
+ { GID_LSL6, -1, 928, -1, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class
+ { GID_LSL6HIRES, 0, 85, 0, "LL6Inv", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // on startup
+ { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area
+ { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game
+ { GID_MOTHERGOOSE, 18, 992, 0, "AIPath", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // DEMO: Called when walking north from mother goose's house two screens
+ { GID_MOTHERGOOSEHIRES,-1,64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later
+ { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above
+ { GID_PEPPER, -1, 894, 0, "Package", "doVerb", -1, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #3040012
+ { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbd0, 0, { WORKAROUND_FAKE, 0 } }, // hq1: going to the brigands hideout
+ { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbe4, 0, { WORKAROUND_FAKE, 0 } }, // qfg1: going to the brigands hideout
+ { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory
+ { GID_QFG2, -1, 701, -1, "Alley", "at", -1, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #3035835 & #3038367
+ { GID_QFG2, -1, 990, 0, "Restore", "doit", -1, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present
+ { GID_QFG2, 260, 260, 0, "abdulS", "changeState",0x2d22, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Abdul is about to enter the house (where you have to hide in the wardrobe), bug #3039891, temps 1 and 2
+ { GID_QFG2, 260, 260, 0, "jabbarS", "changeState",0x2d22, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #3040469, temps 1 and 2
+ { GID_QFG3, 140, 140, 0, "rm140", "init", 0x1008, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #3040460
+ { GID_QFG3, 330, 330, -1, "Teller", "doChild", -1, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #3036390, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #3039774, temp 1)
+ { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #3040624
+ { GID_QFG3, 470, 470, -1, "rm470", "notify", -1, 0, { WORKAROUND_FAKE, 0 } }, // closing the character screen in the Simbani village in the room with the bridge, bug #3040565
+ { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", -1, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen
+ { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu
+ { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happen sometimes in fights
+ { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2
+ { GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser
+ { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens)
+ { GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // sq4cd: called when rummaging in Software Excess bargain bin
+ { GID_SQ4, -1, 928, 0, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // sq4cd: method returns this to the caller
+ { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #3038563
+ { GID_SQ6, 100, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // called when the game starts
+ { GID_SQ6, 100, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu
+ { GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kAbs_workarounds[] = {
+ { GID_HOYLE1, 1, 1, 0, "room1", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers
+ { GID_HOYLE1, 2, 2, 0, "room2", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers
+ { GID_HOYLE1, 3, 3, 0, "room3", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers
+ { GID_QFG1VGA, -1, -1, 0, NULL, "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kCelHigh_workarounds[] = {
+ { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #3037003
+ { GID_PQ2, -1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects
+ { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #3035720
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kCelWide_workarounds[] = {
+ { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #3037003
+ { GID_PQ2, -1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects
+ { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #3035720
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kDeviceInfo_workarounds[] = {
+ { GID_FANMADE, -1, 994, 1, "Game", "save", 0xd1c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Cascade Quest)
+ { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe55, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Demo Quest)
+ { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe57, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (I Want My C64 Back)
+ { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe5c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Most of them)
+ { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xd1c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Cascade Quest)
+ { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xe55, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Demo Quest)
+ { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xe57, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (I Want My C64 Back)
+ { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xe5c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Most of them)
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kDisplay_workarounds[] = {
+ { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object
+ { GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4ae, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id
+ { GID_QFG1, 11, 11, 0, "battle", "<noname90>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id
+ { GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // clicking on catalog in roboter sale - a parameter is an object
+ { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ordering connector in roboter sale - a parameter is an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kDisposeScript_workarounds[] = {
+ { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #3034907
+ { GID_QFG1, -1, 64, 0, "rm64", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object
+ { GID_SQ4, 150, 151, 0, "fightScript", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with vohaul, parameter 0 is an object
+ { GID_SQ4, 150, 152, 0, "driveCloseUp", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kDoSoundFade_workarounds[] = {
+ { GID_CAMELOT, -1, 989, 0, "rmMusic", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called frequently with a NULL reference (i.e. 0:0) - bug #3035149
+ { GID_KQ1, -1, 989, 0, "gameSound", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in several scenes (e.g. graham cracker) with 0:0
+ { GID_KQ4, -1, 989, 0, "mySound", "", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in the demo when trying to open the non-existent menu with 0:0 - bug #3036942
+ { GID_KQ5, 213, 989, 0, "globalSound3", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #3037594
+ { GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object
+ { GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #3034567
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGetAngle_workarounds[] = {
+ { GID_FANMADE, 516, 992, 0, "Motion", "init", -1, 0, { WORKAROUND_IGNORE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects
+ { GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #3034610 & #3041734
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kFindKey_workarounds[] = {
+ { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", -1, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #3035186
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphDrawLine_workarounds[] = {
+ { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter
+ { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter
+ { GID_SQ1, 71, 71, 0, "destroyXenon", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // during the Xenon destruction cutscene (which results in death), gets called with 1 extra parameter - bug #3040894
+ { GID_SQ1, 53, 53, 0, "blastEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when Roger is found and zapped by the cleaning robot, gets called with 1 extra parameter - bug #3040905
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphSaveBox_workarounds[] = {
+ { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter
+ { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #3034485
+ { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above
+ { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution)
+ { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
+ { GID_LSL6, -1, 85, 0, "rScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below
+ { GID_LSL6, -1, 85, 0, "lScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below
+ { GID_LSL6, -1, 86, 0, "LL6Inv", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time
+ // ^^ TODO: check, if this is really a script error or an issue with our restore code
+ { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = {
+ { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = {
+ { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = {
+ { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified
+ { GID_KQ5, -1, 981, 0, "myWindow", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #3036331
+ { GID_KQ5, -1, 995, 0, "invW", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified
+ { GID_KQ5, -1, 995, 0, "", "export 0", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when opening the gem pouch, accidental additional parameter specified - bug #3039395
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = {
+ { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
+ { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kIsObject_workarounds[] = {
+ { GID_GK1, 50, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #3034519
+ { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #3035262
+ { GID_QFG3, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kMemory_workarounds[] = {
+ { GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train, talking to Mr. Augustini, etc. - bug #3034490
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kNewWindow_workarounds[] = {
+ { GID_ECOQUEST, -1, 981, 0, "SysWindow", "<noname178>", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #3035057
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = {
+ { GID_QFG4, 100, 100, 0, "doMovie", "<noname144>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #3034506
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kSetCursor_workarounds[] = {
+ { GID_KQ5, -1, 768, 0, "KQCursor", "init", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kSetPort_workarounds[] = {
+ { GID_LSL6, 740, 740, 0, "rm740", "drawPic", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters
+ { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, bug #3040844
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kStrAt_workarounds[] = {
+ { GID_CASTLEBRAIN, 220, 220, 0, "robotJokes", "animateOnce", -1, 0, { WORKAROUND_FAKE, 0 } }, // when trying to view the terminal at the end of the maze without having collected any robot jokes - bug #3039036
+ { GID_ISLANDBRAIN, 300, 310, 0, "childBreed", "changeState",0x1c7c, 0, { WORKAROUND_FAKE, 0 } }, // when clicking Breed to get the second-generation cyborg hybrid (Standard difficulty), the two parameters are swapped - bug #3037835
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kUnLoad_workarounds[] = {
+ { GID_CAMELOT, 921, 921, 1, "Script", "changeState", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: While showing Camelot (and other places), the reference is invalid - bug #3035000
+ { GID_CAMELOT, 921, 921, 1, "Script", "init", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When being attacked by the boar (and other places), the reference is invalid - bug #3035000
+ { GID_CASTLEBRAIN, 320, 377, 0, "SWord", "upDate", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after solving the cross-word-puzzle, trying to unload invalid reference
+ { GID_CASTLEBRAIN, 320, 377, 0, "theWord", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // 2nd word puzzle, when exiting before solving, trying to unload invalid reference - bug #3034473
+ { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after talking to the dolphin the first time
+ { GID_LAURABOW2, 1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 2, 2, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 4, 4, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: inside the museum, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 6, 6, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the murder, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 7, 7, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the logo is shown, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
+ { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident
+ { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
+ { GID_PQ3, 877, 998, 0, "View", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when getting run over on the freeway, the reference is invalid
+ { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) {
+ EngineState *state = g_sci->getEngineState();
+ ExecStack *lastCall = state->xs;
+ Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment);
+ int curScriptNr = local_script->getScriptNumber();
+
+ if (lastCall->debugLocalCallOffset != -1) {
+ // if lastcall was actually a local call search back for a real call
+ Common::List<ExecStack>::iterator callIterator = state->_executionStack.end();
+ while (callIterator != state->_executionStack.begin()) {
+ callIterator--;
+ ExecStack loopCall = *callIterator;
+ if ((loopCall.debugSelector != -1) || (loopCall.debugExportId != -1)) {
+ lastCall->debugSelector = loopCall.debugSelector;
+ lastCall->debugExportId = loopCall.debugExportId;
+ break;
+ }
+ }
+ }
+
+ Common::String curObjectName = state->_segMan->getObjectName(lastCall->sendp);
+ Common::String curMethodName;
+ const SciGameId gameId = g_sci->getGameId();
+ const int curRoomNumber = state->currentRoomNumber();
+
+ if (lastCall->type == EXEC_STACK_TYPE_CALL) {
+ if (lastCall->debugSelector != -1) {
+ curMethodName = g_sci->getKernel()->getSelectorName(lastCall->debugSelector);
+ } else if (lastCall->debugExportId != -1) {
+ curObjectName = "";
+ curMethodName = curMethodName.printf("export %d", lastCall->debugExportId);
+ }
+ }
+
+ if (workaroundList) {
+ // Search if there is a workaround for this one
+ const SciWorkaroundEntry *workaround;
+ int16 inheritanceLevel = 0;
+ Common::String searchObjectName = curObjectName;
+ reg_t searchObject = lastCall->sendp;
+ do {
+ workaround = workaroundList;
+ while (workaround->methodName) {
+ if (workaround->gameId == gameId
+ && ((workaround->scriptNr == -1) || (workaround->scriptNr == curScriptNr))
+ && ((workaround->roomNr == -1) || (workaround->roomNr == curRoomNumber))
+ && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel))
+ && ((workaround->objectName == NULL) || (workaround->objectName == searchObjectName))
+ && workaround->methodName == curMethodName && workaround->localCallOffset == lastCall->debugLocalCallOffset
+ && ((workaround->index == -1) || (workaround->index == index))) {
+ // Workaround found
+ return workaround->newValue;
+ }
+ workaround++;
+ }
+
+ // Go back to the parent
+ inheritanceLevel++;
+ searchObject = state->_segMan->getObject(searchObject)->getSuperClassSelector();
+ if (!searchObject.isNull())
+ searchObjectName = state->_segMan->getObjectName(searchObject);
+ } while (!searchObject.isNull()); // no parent left?
+ }
+
+ // give caller origin data
+ trackOrigin->objectName = curObjectName;
+ trackOrigin->methodName = curMethodName;
+ trackOrigin->scriptNr = curScriptNr;
+ trackOrigin->localCallOffset = lastCall->debugLocalCallOffset;
+
+ SciWorkaroundSolution noneFound;
+ noneFound.type = WORKAROUND_NONE;
+ noneFound.value = 0;
+ return noneFound;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
new file mode 100644
index 0000000000..220ffd7fda
--- /dev/null
+++ b/engines/sci/engine/workarounds.h
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 SCI_ENGINE_WORKAROUNDS_H
+#define SCI_ENGINE_WORKAROUNDS_H
+
+#include "sci/engine/vm_types.h"
+#include "sci/sci.h"
+
+namespace Sci {
+
+enum SciWorkaroundType {
+ WORKAROUND_NONE, // only used by terminator or when no workaround was found
+ WORKAROUND_IGNORE, // ignore kernel call
+ WORKAROUND_STILLCALL, // still do kernel call
+ WORKAROUND_FAKE // fake kernel call / replace temp value / fake opcode
+};
+
+struct SciTrackOriginReply {
+ int scriptNr;
+ Common::String objectName;
+ Common::String methodName;
+ int localCallOffset;
+};
+
+struct SciWorkaroundSolution {
+ SciWorkaroundType type;
+ uint16 value;
+};
+
+/**
+ * A structure describing a 'workaround' for a SCI script bug.
+ *
+ * Arrays of SciWorkaroundEntry instances are terminated by
+ * a fake entry in which "objectName" is NULL.
+ */
+struct SciWorkaroundEntry {
+ SciGameId gameId;
+ int roomNr;
+ int scriptNr;
+ int16 inheritanceLevel;
+ const char *objectName;
+ const char *methodName;
+ int localCallOffset;
+ int index;
+ SciWorkaroundSolution newValue;
+};
+
+extern const SciWorkaroundEntry opcodeDivWorkarounds[];
+extern const SciWorkaroundEntry opcodeDptoaWorkarounds[];
+extern const SciWorkaroundEntry opcodeGeWorkarounds[];
+extern const SciWorkaroundEntry opcodeLeWorkarounds[];
+extern const SciWorkaroundEntry opcodeLsiWorkarounds[];
+extern const SciWorkaroundEntry opcodeMulWorkarounds[];
+extern const SciWorkaroundEntry opcodeAndWorkarounds[];
+extern const SciWorkaroundEntry opcodeOrWorkarounds[];
+extern const SciWorkaroundEntry uninitializedReadWorkarounds[];
+extern const SciWorkaroundEntry kAbs_workarounds[];
+extern const SciWorkaroundEntry kCelHigh_workarounds[];
+extern const SciWorkaroundEntry kCelWide_workarounds[];
+extern const SciWorkaroundEntry kDeviceInfo_workarounds[];
+extern const SciWorkaroundEntry kDisplay_workarounds[];
+extern const SciWorkaroundEntry kDisposeScript_workarounds[];
+extern const SciWorkaroundEntry kDoSoundFade_workarounds[];
+extern const SciWorkaroundEntry kFindKey_workarounds[];
+extern const SciWorkaroundEntry kGetAngle_workarounds[];
+extern const SciWorkaroundEntry kGraphDrawLine_workarounds[];
+extern const SciWorkaroundEntry kGraphSaveBox_workarounds[];
+extern const SciWorkaroundEntry kGraphRestoreBox_workarounds[];
+extern const SciWorkaroundEntry kGraphUpdateBox_workarounds[];
+extern const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[];
+extern const SciWorkaroundEntry kGraphFillBoxAny_workarounds[];
+extern const SciWorkaroundEntry kGraphRedrawBox_workarounds[];
+extern const SciWorkaroundEntry kIsObject_workarounds[];
+extern const SciWorkaroundEntry kMemory_workarounds[];
+extern const SciWorkaroundEntry kNewWindow_workarounds[];
+extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[];
+extern const SciWorkaroundEntry kSetCursor_workarounds[];
+extern const SciWorkaroundEntry kSetPort_workarounds[];
+extern const SciWorkaroundEntry kStrAt_workarounds[];
+extern const SciWorkaroundEntry kUnLoad_workarounds[];
+
+extern SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin);
+
+} // End of namespace Sci
+
+#endif // SCI_ENGINE_WORKAROUNDS_H
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 53f4675f56..5923e501cf 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -25,6 +25,7 @@
#include "common/system.h"
#include "common/events.h"
+#include "common/file.h"
#include "sci/sci.h"
#include "sci/event.h"
@@ -34,33 +35,51 @@
namespace Sci {
-#define SCANCODE_ROWS_NR 3
+EventManager::EventManager(bool fontIsExtended) : _fontIsExtended(fontIsExtended), _modifierStates(0) {
-SciEvent::SciEvent(ResourceManager *resMan) {
- // Check, if font of current game includes extended chars
- _fontIsExtended = resMan->detectFontExtended();
+ if (getSciVersion() >= SCI_VERSION_1_MIDDLE) {
+ _usesNewKeyboardDirectionType = true;
+ } else if (getSciVersion() <= SCI_VERSION_01) {
+ _usesNewKeyboardDirectionType = false;
+ } else {
+ // they changed this somewhere inbetween SCI1EGA/EARLY
+ _usesNewKeyboardDirectionType = false;
+
+ // We are looking if script 933 exists, that one has the PseudoMouse class in it that handles it
+ // The good thing is that PseudoMouse seems to only exists in games that use the new method
+ if (g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 933)))
+ _usesNewKeyboardDirectionType = true;
+ // Checking the keyboard driver size in here would also be a valid method, but the driver is only available
+ // in PC versions of the game
+ }
+}
+
+EventManager::~EventManager() {
}
-SciEvent::~SciEvent() {
+bool EventManager::getUsesNewKeyboardDirectionType() {
+ return _usesNewKeyboardDirectionType;
}
-struct scancode_row {
+struct ScancodeRow {
int offset;
const char *keys;
-} scancode_rows[SCANCODE_ROWS_NR] = {
+};
+
+static const ScancodeRow s_scancodeRows[] = {
{0x10, "QWERTYUIOP[]"},
{0x1e, "ASDFGHJKL;'\\"},
{0x2c, "ZXCVBNM,./"}
};
-int SciEvent::altify (int ch) {
+static int altify(int ch) {
// Calculates a PC keyboard scancode from a character */
int row;
int c = toupper((char)ch);
- for (row = 0; row < SCANCODE_ROWS_NR; row++) {
- const char *keys = scancode_rows[row].keys;
- int offset = scancode_rows[row].offset;
+ for (row = 0; row < ARRAYSIZE(s_scancodeRows); row++) {
+ const char *keys = s_scancodeRows[row].keys;
+ int offset = s_scancodeRows[row].offset;
while (*keys) {
if (*keys == c)
@@ -74,7 +93,8 @@ int SciEvent::altify (int ch) {
return ch;
}
-int SciEvent::numlockify (int c) {
+/*
+static int numlockify(int c) {
switch (c) {
case SCI_KEY_DELETE:
return '.';
@@ -102,6 +122,7 @@ int SciEvent::numlockify (int c) {
return c; // Unchanged
}
}
+*/
static const byte codepagemap_88591toDOS[0x80] = {
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
@@ -114,9 +135,8 @@ static const byte codepagemap_88591toDOS[0x80] = {
'?', 0xa4, 0x95, 0xa2, 0x93, '?', 0x94, '?', '?', 0x97, 0xa3, 0x96, 0x81, '?', '?', 0x98 // 0xFx
};
-sciEvent SciEvent::getFromScummVM() {
- static int _modifierStates = 0; // FIXME: Avoid non-const global vars
- sciEvent input = { SCI_EVENT_NONE, 0, 0, 0 };
+SciEvent EventManager::getScummVMEvent() {
+ SciEvent input = { SCI_EVENT_NONE, 0, 0, 0 };
Common::EventManager *em = g_system->getEventManager();
Common::Event ev;
@@ -293,6 +313,10 @@ sciEvent SciEvent::getFromScummVM() {
input.type = SCI_EVENT_MOUSE_PRESS;
input.data = 2;
break;
+ case Common::EVENT_MBUTTONDOWN:
+ input.type = SCI_EVENT_MOUSE_PRESS;
+ input.data = 3;
+ break;
case Common::EVENT_LBUTTONUP:
input.type = SCI_EVENT_MOUSE_RELEASE;
input.data = 1;
@@ -301,6 +325,10 @@ sciEvent SciEvent::getFromScummVM() {
input.type = SCI_EVENT_MOUSE_RELEASE;
input.data = 2;
break;
+ case Common::EVENT_MBUTTONUP:
+ input.type = SCI_EVENT_MOUSE_RELEASE;
+ input.data = 3;
+ break;
// Misc events
case Common::EVENT_QUIT:
@@ -315,24 +343,30 @@ sciEvent SciEvent::getFromScummVM() {
return input;
}
-sciEvent SciEvent::get(unsigned int mask) {
- //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 };
- sciEvent event = { 0, 0, 0, 0 };
+void EventManager::updateScreen() {
+ // Update the screen here, since it's called very often.
+ // Throttle the screen update rate to 60fps.
+ if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) {
+ g_system->updateScreen();
+ g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis();
+ }
+}
- // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position
+SciEvent EventManager::getSciEvent(unsigned int mask) {
+ //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 };
+ SciEvent event = { 0, 0, 0, 0 };
- // Update the screen here, since it's called very often
- g_system->updateScreen();
+ EventManager::updateScreen();
// Get all queued events from graphics driver
do {
- event = getFromScummVM();
+ event = getScummVMEvent();
if (event.type != SCI_EVENT_NONE)
_events.push_back(event);
} while (event.type != SCI_EVENT_NONE);
// Search for matching event in queue
- Common::List<sciEvent>::iterator iter = _events.begin();
+ Common::List<SciEvent>::iterator iter = _events.begin();
while (iter != _events.end() && !((*iter).type & mask))
++iter;
@@ -382,14 +416,13 @@ sciEvent SciEvent::get(unsigned int mask) {
return event;
}
-void SciEvent::sleep(uint32 msecs) {
+void SciEngine::sleep(uint32 msecs) {
uint32 time;
const uint32 wakeup_time = g_system->getMillis() + msecs;
while (true) {
// let backend process events and update the screen
- get(SCI_EVENT_PEEK);
- // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position
+ _eventMan->getSciEvent(SCI_EVENT_PEEK);
time = g_system->getMillis();
if (time + 10 < wakeup_time) {
g_system->delayMillis(10);
diff --git a/engines/sci/event.h b/engines/sci/event.h
index 9301b1ca09..fade7dd337 100644
--- a/engines/sci/event.h
+++ b/engines/sci/event.h
@@ -23,31 +23,29 @@
*
*/
-#ifndef SCI_ENGINE_EVENT_H
-#define SCI_ENGINE_EVENT_H
+#ifndef SCI_EVENT_H
+#define SCI_EVENT_H
#include "common/list.h"
namespace Sci {
-#define SCI_INPUT_DEFAULT_CLOCKTIME 100000
-#define SCI_INPUT_DEFAULT_REDRAWTIME 30000
-
-
-struct sciEvent {
+struct SciEvent {
short type;
short data;
short modifiers;
- short character; /* for keyboard events: 'data' after applying
- ** the effects of 'modifiers', e.g. if
- ** type == SCI_EVT_KEYBOARD
- ** data == 'a'
- ** buckybits == SCI_EVM_LSHIFT
- ** then
- ** character == 'A'
- ** For 'Alt', characters are interpreted by their
- ** PC keyboard scancodes.
- */
+ /**
+ * For keyboard events: 'data' after applying
+ * the effects of 'modifiers', e.g. if
+ * type == SCI_EVT_KEYBOARD
+ * data == 'a'
+ * buckybits == SCI_EVM_LSHIFT
+ * then
+ * character == 'A'
+ * For 'Alt', characters are interpreted by their
+ * PC keyboard scancodes.
+ */
+ short character;
};
/*Values for type*/
@@ -55,7 +53,7 @@ struct sciEvent {
#define SCI_EVENT_MOUSE_PRESS (1<<0)
#define SCI_EVENT_MOUSE_RELEASE (1<<1)
#define SCI_EVENT_KEYBOARD (1<<2)
-#define SCI_EVENT_JOYSTICK (1<<6)
+#define SCI_EVENT_DIRECTION (1<<6)
#define SCI_EVENT_SAID (1<<7)
/*Fake values for other events*/
#define SCI_EVENT_ERROR (1<<10)
@@ -111,25 +109,23 @@ struct sciEvent {
#define SCI_KEYMOD_NO_FOOLOCK (~(SCI_KEYMOD_SCRLOCK | SCI_KEYMOD_NUMLOCK | SCI_KEYMOD_CAPSLOCK | SCI_KEYMOD_INSERT))
#define SCI_KEYMOD_ALL 0xFF
-class SciEvent {
+class EventManager {
public:
- SciEvent(ResourceManager *resMgr);
- ~SciEvent();
-
- sciEvent get(unsigned int mask);
+ EventManager(bool fontIsExtended);
+ ~EventManager();
- void sleep(uint32 msecs);
+ void updateScreen();
+ SciEvent getSciEvent(unsigned int mask);
+ bool getUsesNewKeyboardDirectionType();
private:
- int altify (int ch);
- int shiftify (int c);
- int numlockify (int c);
- sciEvent getFromScummVM();
+ SciEvent getScummVMEvent();
- ResourceManager *_resMan;
+ const bool _fontIsExtended;
+ int _modifierStates;
+ Common::List<SciEvent> _events;
- bool _fontIsExtended;
- Common::List<sciEvent> _events;
+ bool _usesNewKeyboardDirectionType;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 71c7b7dd7f..ab4362cda9 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -28,6 +28,8 @@
#include "graphics/primitives.h"
#include "sci/sci.h"
+#include "sci/event.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -35,6 +37,7 @@
#include "sci/graphics/cursor.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/paint16.h"
+#include "sci/graphics/palette.h"
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/transitions.h"
@@ -48,28 +51,25 @@ GfxAnimate::GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, Gfx
}
GfxAnimate::~GfxAnimate() {
- free(_listData);
- free(_lastCastData);
}
void GfxAnimate::init() {
- _listData = NULL;
- _listCount = 0;
- _lastCastData = NULL;
- _lastCastCount = 0;
+ _lastCastData.clear();
_ignoreFastCast = false;
// fastCast object is not found in any SCI games prior SCI1
if (getSciVersion() <= SCI_VERSION_01)
_ignoreFastCast = true;
// Also if fastCast object exists at gamestartup, we can assume that the interpreter doesnt do kAnimate aborts
- // (found in larry 1)
- if (!_s->_segMan->findObjectByName("fastCast").isNull())
- _ignoreFastCast = true;
+ // (found in Larry 1)
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ if (!_s->_segMan->findObjectByName("fastCast").isNull())
+ _ignoreFastCast = true;
+ }
}
void GfxAnimate::disposeLastCast() {
- _lastCastCount = 0;
+ _lastCastData.clear();
}
bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
@@ -84,20 +84,27 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
if (!_ignoreFastCast) {
// Check if the game has a fastCast object set
// if we don't abort kAnimate processing, at least in kq5 there will be animation cels drawn into speech boxes.
- reg_t global84 = _s->script_000->_localsBlock->_locals[84];
-
- if (!global84.isNull()) {
- if (!strcmp(_s->_segMan->getObjectName(global84), "fastCast"))
+ if (!_s->variables[VAR_GLOBAL][84].isNull()) {
+ if (!strcmp(_s->_segMan->getObjectName(_s->variables[VAR_GLOBAL][84]), "fastCast"))
return false;
}
}
- signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal));
+ signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if (!(signal & kSignalFrozen)) {
// Call .doit method of that object
- invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0);
- // Lookup node again, since the nodetable it was in may have been reallocated
- curNode = _s->_segMan->lookupNode(curAddress);
+ invokeSelector(_s, curObject, SELECTOR(doit), argc, argv, 0);
+
+ // If a game is being loaded, stop processing
+ if (_s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return true; // Stop processing
+
+ // Lookup node again, since the nodetable it was in may have been reallocated.
+ // The node might have been deallocated at this point (e.g. LSL2, room 42),
+ // in which case the node reference will be null and the loop will stop below.
+ // If the node is deleted from kDeleteKey, it won't have a successor node, thus
+ // list processing will stop here (which is what SSCI does).
+ curNode = _s->_segMan->lookupNode(curAddress, false);
}
if (curNode) {
@@ -108,141 +115,163 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
return true;
}
-bool sortHelper(const AnimateEntry* entry1, const AnimateEntry* entry2) {
- return (entry1->y == entry2->y) ? (entry1->z < entry2->z) : (entry1->y < entry2->y);
+bool sortHelper(const AnimateEntry &entry1, const AnimateEntry &entry2) {
+ if (entry1.y == entry2.y) {
+ // if both y and z are the same, use the order we were given originally
+ // this is needed for special cases like iceman room 35
+ if (entry1.z == entry2.z)
+ return entry1.givenOrderNo < entry2.givenOrderNo;
+ else
+ return entry1.z < entry2.z;
+ }
+ return entry1.y < entry2.y;
}
void GfxAnimate::makeSortedList(List *list) {
reg_t curAddress = list->first;
Node *curNode = _s->_segMan->lookupNode(curAddress);
- reg_t curObject;
- AnimateEntry *listEntry;
- int16 listNr, listCount = 0;
-
- // Count the list entries
- while (curNode) {
- listCount++;
- curAddress = curNode->succ;
- curNode = _s->_segMan->lookupNode(curAddress);
- }
+ int16 listNr;
+ // Clear lists
_list.clear();
-
- // No entries -> exit immediately
- if (listCount == 0)
- return;
-
- // Adjust list size, if needed
- if ((_listData == NULL) || (_listCount < listCount)) {
- free(_listData);
- _listData = (AnimateEntry *)malloc(listCount * sizeof(AnimateEntry));
- if (!_listData)
- error("Could not allocate memory for _listData");
- _listCount = listCount;
-
- free(_lastCastData);
- _lastCastData = (AnimateEntry *)malloc(listCount * sizeof(AnimateEntry));
- if (!_lastCastData)
- error("Could not allocate memory for _lastCastData");
- _lastCastCount = 0;
- }
+ _lastCastData.clear();
// Fill the list
- curAddress = list->first;
- curNode = _s->_segMan->lookupNode(curAddress);
- listEntry = _listData;
- for (listNr = 0; listNr < listCount; listNr++) {
- curObject = curNode->value;
- listEntry->object = curObject;
+ for (listNr = 0; curNode != 0; listNr++) {
+ AnimateEntry listEntry;
+ const reg_t curObject = curNode->value;
+ listEntry.object = curObject;
// Get data from current object
- listEntry->viewId = GET_SEL32V(_s->_segMan, curObject, SELECTOR(view));
- listEntry->loopNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(loop));
- listEntry->celNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(cel));
- listEntry->paletteNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(palette));
- listEntry->x = GET_SEL32V(_s->_segMan, curObject, SELECTOR(x));
- listEntry->y = GET_SEL32V(_s->_segMan, curObject, SELECTOR(y));
- listEntry->z = GET_SEL32V(_s->_segMan, curObject, SELECTOR(z));
- listEntry->priority = GET_SEL32V(_s->_segMan, curObject, SELECTOR(priority));
- listEntry->signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal));
+ listEntry.givenOrderNo = listNr;
+ listEntry.viewId = readSelectorValue(_s->_segMan, curObject, SELECTOR(view));
+ listEntry.loopNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(loop));
+ listEntry.celNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(cel));
+ listEntry.paletteNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(palette));
+ listEntry.x = readSelectorValue(_s->_segMan, curObject, SELECTOR(x));
+ listEntry.y = readSelectorValue(_s->_segMan, curObject, SELECTOR(y));
+ listEntry.z = readSelectorValue(_s->_segMan, curObject, SELECTOR(z));
+ listEntry.priority = readSelectorValue(_s->_segMan, curObject, SELECTOR(priority));
+ listEntry.signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if (getSciVersion() >= SCI_VERSION_1_1) {
// Cel scaling
- listEntry->scaleSignal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleSignal));
- if (listEntry->scaleSignal & kScaleSignalDoScaling) {
- listEntry->scaleX = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleX));
- listEntry->scaleY = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleY));
+ listEntry.scaleSignal = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleSignal));
+ if (listEntry.scaleSignal & kScaleSignalDoScaling) {
+ listEntry.scaleX = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX));
+ listEntry.scaleY = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY));
} else {
- listEntry->scaleX = 128;
- listEntry->scaleY = 128;
+ listEntry.scaleX = 128;
+ listEntry.scaleY = 128;
}
- // TODO
- // On scaleSignal bit 1 sierra sci does some stuff with global var 2, current Port
- // and some other stuff and sets scaleX/Y accordingly. It seems this functionality is needed in at
- // least sq5 right when starting the game before wilco exists the room. Currently we dont get scaling
- // but sierra sci does scaling there. I dont fully understand the code yet, that's why i didnt implement
- // anything.
} else {
- listEntry->scaleSignal = 0;
- listEntry->scaleX = 128;
- listEntry->scaleY = 128;
+ listEntry.scaleSignal = 0;
+ listEntry.scaleX = 128;
+ listEntry.scaleY = 128;
}
- // listEntry->celRect is filled in AnimateFill()
- listEntry->showBitsFlag = false;
+ // listEntry.celRect is filled in AnimateFill()
+ listEntry.showBitsFlag = false;
_list.push_back(listEntry);
- listEntry++;
curAddress = curNode->succ;
curNode = _s->_segMan->lookupNode(curAddress);
}
+ // Possible TODO: As noted in the comment in sortHelper we actually
+ // require a stable sorting algorithm here. Since Common::sort is not stable
+ // at the time of writing this comment, we work around that in our ordering
+ // comparator. If that changes in the future or we want to use some
+ // stable sorting algorithm here, we should change that.
+ // In that case we should test such changes intensively. A good place to test stable sort
+ // is iceman, cupboard within the submarine. If sort isn't stable, the cupboard will be
+ // half-open, half-closed. Of course that's just one of many special cases.
+
// Now sort the list according y and z (descending)
Common::sort(_list.begin(), _list.end(), sortHelper);
}
void GfxAnimate::fill(byte &old_picNotValid) {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
GfxView *view = NULL;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
// Get the corresponding view
- view = _cache->getView(listEntry->viewId);
+ view = _cache->getView(it->viewId);
// adjust loop and cel, if any of those is invalid
- if (listEntry->loopNo >= view->getLoopCount()) {
- listEntry->loopNo = 0;
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo);
+ if (it->loopNo >= view->getLoopCount()) {
+ it->loopNo = 0;
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), it->loopNo);
+ }
+ if (it->celNo >= view->getCelCount(it->loopNo)) {
+ it->celNo = 0;
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), it->celNo);
+ }
+
+ // Process global scaling, if needed
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ if (it->scaleSignal & kScaleSignalGlobalScaling) {
+ // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY
+ int16 maxScale = readSelectorValue(_s->_segMan, curObject, SELECTOR(maxScale));
+ int16 celHeight = view->getHeight(it->loopNo, it->celNo);
+ int16 maxCelHeight = (maxScale * celHeight) >> 7;
+ reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object
+ int16 vanishingY = readSelectorValue(_s->_segMan, globalVar2, SELECTOR(vanishingY));
+
+ int16 fixedPortY = _ports->getPort()->rect.bottom - vanishingY;
+ int16 fixedEntryY = it->y - vanishingY;
+ if (!fixedEntryY)
+ fixedEntryY = 1;
+
+ if ((celHeight == 0) || (fixedPortY == 0))
+ error("global scaling panic");
+
+ it->scaleY = ( maxCelHeight * fixedEntryY ) / fixedPortY;
+ it->scaleY = (it->scaleY * 128) / celHeight;
+
+ it->scaleX = it->scaleY;
+
+ // and set objects scale selectors
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), it->scaleX);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), it->scaleY);
+ }
}
- if (listEntry->celNo >= view->getCelCount(listEntry->loopNo)) {
- listEntry->celNo = 0;
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo);
+
+ if (!view->isScaleable()) {
+ // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable
+ // this "feature" was removed in later versions of SCI1.1
+ it->scaleSignal = 0;
+ it->scaleY = it->scaleX = 128;
}
+ bool setNsRect = true;
+
// Create rect according to coordinates and given cel
- if (listEntry->scaleSignal & kScaleSignalDoScaling) {
- view->getCelScaledRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, listEntry->scaleX, listEntry->scaleY, &listEntry->celRect);
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
+ // when being scaled, only set nsRect, if object will get drawn
+ if ((signal & kSignalHidden) && !(signal & kSignalAlwaysUpdate))
+ setNsRect = false;
} else {
- view->getCelRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, &listEntry->celRect);
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ }
+ if (setNsRect) {
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), it->celRect.bottom);
}
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsLeft), listEntry->celRect.left);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsTop), listEntry->celRect.top);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsRight), listEntry->celRect.right);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsBottom), listEntry->celRect.bottom);
-
- signal = listEntry->signal;
// Calculate current priority according to y-coordinate
if (!(signal & kSignalFixedPriority)) {
- listEntry->priority = _ports->kernelCoordinateToPriority(listEntry->y);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority);
+ it->priority = _ports->kernelCoordinateToPriority(it->y);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), it->priority);
}
if (signal & kSignalNoUpdate) {
@@ -251,205 +280,182 @@ void GfxAnimate::fill(byte &old_picNotValid) {
|| (!(signal & kSignalHidden) && signal & kSignalRemoveView)
|| (signal & kSignalAlwaysUpdate))
old_picNotValid++;
- signal &= 0xFFFF ^ kSignalStopUpdate;
+ signal &= ~kSignalStopUpdate;
} else {
if (signal & kSignalStopUpdate || signal & kSignalAlwaysUpdate)
old_picNotValid++;
- signal &= 0xFFFF ^ kSignalForceUpdate;
+ signal &= ~kSignalForceUpdate;
}
- listEntry->signal = signal;
-
- listIterator++;
+ it->signal = signal;
}
}
void GfxAnimate::update() {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
reg_t bitsHandle;
Common::Rect rect;
- AnimateList::iterator listIterator;
- AnimateList::iterator listBegin = _list.begin();
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
// Remove all no-update cels, if requested
- listIterator = _list.reverse_begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.reverse_begin(); it != end; --it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalNoUpdate) {
if (!(signal & kSignalRemoveView)) {
- bitsHandle = GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits));
+ bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits));
if (_screen->_picNotValid != 1) {
_paint16->bitsRestore(bitsHandle);
- listEntry->showBitsFlag = true;
+ it->showBitsFlag = true;
} else {
_paint16->bitsFree(bitsHandle);
}
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
}
- signal &= 0xFFFF ^ kSignalForceUpdate;
- signal &= signal & kSignalViewUpdated ? 0xFFFF ^ (kSignalViewUpdated | kSignalNoUpdate) : 0xFFFF;
+ signal &= ~kSignalForceUpdate;
+ if (signal & kSignalViewUpdated)
+ signal &= ~(kSignalViewUpdated | kSignalNoUpdate);
} else if (signal & kSignalStopUpdate) {
- signal = (signal & (0xFFFF ^ kSignalStopUpdate)) | kSignalNoUpdate;
+ signal &= ~kSignalStopUpdate;
+ signal |= kSignalNoUpdate;
}
- listEntry->signal = signal;
- listIterator--;
+ it->signal = signal;
}
// Draw always-update cels
- listIterator = listBegin;
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalAlwaysUpdate) {
// draw corresponding cel
- _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY);
- listEntry->showBitsFlag = true;
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
+ it->showBitsFlag = true;
- signal &= 0xFFFF ^ (kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
+ signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
if ((signal & kSignalIgnoreActor) == 0) {
- rect = listEntry->celRect;
- rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(listEntry->priority) - 1, rect.top, rect.bottom - 1);
+ rect = it->celRect;
+ rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
- listEntry->signal = signal;
+ it->signal = signal;
}
- listIterator++;
}
// Saving background for all NoUpdate-cels
- listIterator = listBegin;
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalNoUpdate) {
if (signal & kSignalHidden) {
signal |= kSignalRemoveView;
} else {
- signal &= 0xFFFF ^ kSignalRemoveView;
+ signal &= ~kSignalRemoveView;
if (signal & kSignalIgnoreActor)
- bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
+ bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
else
- bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL);
- PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
+ writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
}
- listEntry->signal = signal;
+ it->signal = signal;
}
- listIterator++;
}
// Draw NoUpdate cels
- listIterator = listBegin;
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalNoUpdate && !(signal & kSignalHidden)) {
// draw corresponding cel
- _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY);
- listEntry->showBitsFlag = true;
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
+ it->showBitsFlag = true;
- if ((signal & kSignalIgnoreActor) == 0) {
- rect = listEntry->celRect;
- rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(listEntry->priority) - 1, rect.top, rect.bottom - 1);
+ if (!(signal & kSignalIgnoreActor)) {
+ rect = it->celRect;
+ rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
}
- listIterator++;
}
}
void GfxAnimate::drawCels() {
reg_t curObject;
- AnimateEntry *listEntry;
- AnimateEntry *lastCastEntry = _lastCastData;
uint16 signal;
reg_t bitsHandle;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
- _lastCastCount = 0;
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
+ _lastCastData.clear();
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) {
// Save background
- bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL);
- PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
+ writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
// draw corresponding cel
- _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY);
- listEntry->showBitsFlag = true;
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
+ it->showBitsFlag = true;
if (signal & kSignalRemoveView) {
- signal &= 0xFFFF ^ kSignalRemoveView;
+ signal &= ~kSignalRemoveView;
}
- listEntry->signal = signal;
+ it->signal = signal;
// Remember that entry in lastCast
- memcpy(lastCastEntry, listEntry, sizeof(AnimateEntry));
- lastCastEntry++; _lastCastCount++;
+ _lastCastData.push_back(*it);
}
- listIterator++;
}
}
void GfxAnimate::updateScreen(byte oldPicNotValid) {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
Common::Rect lsRect;
Common::Rect workerRect;
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
- if (listEntry->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) ||
+ if (it->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) ||
(!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) {
- lsRect.left = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft));
- lsRect.top = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop));
- lsRect.right = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight));
- lsRect.bottom = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom));
+ lsRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft));
+ lsRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop));
+ lsRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight));
+ lsRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom));
workerRect = lsRect;
- workerRect.clip(listEntry->celRect);
+ workerRect.clip(it->celRect);
if (!workerRect.isEmpty()) {
workerRect = lsRect;
- workerRect.extend(listEntry->celRect);
+ workerRect.extend(it->celRect);
} else {
_paint16->bitsShow(lsRect);
- workerRect = listEntry->celRect;
+ workerRect = it->celRect;
}
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft), workerRect.left);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop), workerRect.top);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight), workerRect.right);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom), workerRect.bottom);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), it->celRect.bottom);
+ // may get used for debugging
+ //_paint16->frameRect(workerRect);
_paint16->bitsShow(workerRect);
if (signal & kSignalHidden) {
- listEntry->signal |= kSignalRemoveView;
+ it->signal |= kSignalRemoveView;
}
}
-
- listIterator++;
}
// use this for debug purposes
// _screen->copyToScreen();
@@ -457,64 +463,53 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
- // This has to be done in a separate loop. At least in sq1 some .dispose modifies FIXEDLOOP flag in signal for
- // another object. In that case we would overwrite the new signal with our version of the old signal
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ // This has to be done in a separate loop. At least in sq1 some .dispose
+ // modifies FIXEDLOOP flag in signal for another object. In that case we
+ // would overwrite the new signal with our version of the old signal.
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
// Finally update signal
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(signal), signal);
- listIterator++;
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal);
}
- listIterator = _list.reverse_begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- // We read out signal here again, this is not by accident but to ensure that we got an up-to-date signal
- signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal));
+ for (it = _list.reverse_begin(); it != end; --it) {
+ curObject = it->object;
+ // We read out signal here again, this is not by accident but to ensure
+ // that we got an up-to-date signal
+ signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) {
- _paint16->bitsRestore(GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits)));
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ _paint16->bitsRestore(readSelector(_s->_segMan, curObject, SELECTOR(underBits)));
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
}
if (signal & kSignalDisposeMe) {
// Call .delete_ method of that object
- invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0);
+ invokeSelector(_s, curObject, SELECTOR(delete_), argc, argv, 0);
}
- listIterator--;
}
}
void GfxAnimate::reAnimate(Common::Rect rect) {
- AnimateEntry *lastCastEntry;
- uint16 lastCastCount;
-
- if (_lastCastCount > 0) {
- lastCastEntry = _lastCastData;
- lastCastCount = _lastCastCount;
- while (lastCastCount > 0) {
- lastCastEntry->castHandle = _paint16->bitsSave(lastCastEntry->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
- _paint16->drawCel(lastCastEntry->viewId, lastCastEntry->loopNo, lastCastEntry->celNo, lastCastEntry->celRect, lastCastEntry->priority, lastCastEntry->paletteNo, lastCastEntry->scaleX, lastCastEntry->scaleY);
- lastCastEntry++; lastCastCount--;
+ if (!_lastCastData.empty()) {
+ AnimateArray::iterator it;
+ AnimateArray::iterator end = _lastCastData.end();
+ for (it = _lastCastData.begin(); it != end; ++it) {
+ it->castHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
}
_paint16->bitsShow(rect);
// restoring
- lastCastCount = _lastCastCount;
- while (lastCastCount > 0) {
- lastCastEntry--;
- _paint16->bitsRestore(lastCastEntry->castHandle);
- lastCastCount--;
+ while (it != _lastCastData.begin()) { // FIXME: HACK, this iterator use is not very safe
+ it--;
+ _paint16->bitsRestore(it->castHandle);
}
} else {
_paint16->bitsShow(rect);
@@ -523,43 +518,46 @@ void GfxAnimate::reAnimate(Common::Rect rect) {
void GfxAnimate::addToPicDrawCels() {
reg_t curObject;
- AnimateEntry *listEntry;
GfxView *view = NULL;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
- if (listEntry->priority == -1)
- listEntry->priority = _ports->kernelCoordinateToPriority(listEntry->y);
+ if (it->priority == -1)
+ it->priority = _ports->kernelCoordinateToPriority(it->y);
// Get the corresponding view
- view = _cache->getView(listEntry->viewId);
+ view = _cache->getView(it->viewId);
// Create rect according to coordinates and given cel
- view->getCelRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, &listEntry->celRect);
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
// draw corresponding cel
- _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo);
- if ((listEntry->signal & kSignalIgnoreActor) == 0) {
- listEntry->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(listEntry->priority) - 1, listEntry->celRect.top, listEntry->celRect.bottom - 1);
- _paint16->fillRect(listEntry->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo);
+ if ((it->signal & kSignalIgnoreActor) == 0) {
+ it->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, it->celRect.top, it->celRect.bottom - 1);
+ _paint16->fillRect(it->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
-
- listIterator++;
}
}
-void GfxAnimate::addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control) {
+void GfxAnimate::addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y, int16 priority, int16 control) {
GfxView *view = _cache->getView(viewId);
Common::Rect celRect;
+ if (priority == -1)
+ priority = _ports->kernelCoordinateToPriority(y);
+
// Create rect according to coordinates and given cel
- view->getCelRect(loopNo, celNo, leftPos, topPos, priority, &celRect);
+ view->getCelRect(loopNo, celNo, x, y, 0, celRect);
_paint16->drawCel(view, loopNo, celNo, celRect, priority, 0);
+
+ if (control != -1) {
+ celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(priority) - 1, celRect.top, celRect.bottom - 1);
+ _paint16->fillRect(celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, control);
+ }
}
@@ -575,14 +573,14 @@ void GfxAnimate::animateShowPic() {
_transitions->doit(picRect);
if (previousCursorState)
_cursor->kernelShow();
-
- // We set SCI1.1 priority band information here
- _ports->priorityBandsRecall();
}
void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv) {
byte old_picNotValid = _screen->_picNotValid;
+ if (getSciVersion() >= SCI_VERSION_1_1)
+ _palette->palVaryUpdate();
+
if (listReference.isNull()) {
disposeLastCast();
if (_screen->_picNotValid)
@@ -597,6 +595,9 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
if (cycle) {
if (!invoke(list, argc, argv))
return;
+
+ // Look up the list again, as it may have been modified
+ list = _s->_segMan->lookupList(listReference);
}
Port *oldPort = _ports->setPort((Port *)_ports->_picWind);
@@ -606,9 +607,9 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
fill(old_picNotValid);
if (old_picNotValid) {
- // beginUpdate()/endUpdate() were introduced SCI1
- // calling those for SCI0 will work most of the time but breaks minor stuff like percentage bar of qfg1ega
- // at the character skill screen
+ // beginUpdate()/endUpdate() were introduced SCI1.
+ // Calling those for SCI0 will work most of the time but breaks minor
+ // stuff like percentage bar of qfg1ega at the character skill screen.
if (getSciVersion() >= SCI_VERSION_1_EGA)
_ports->beginUpdate(_ports->_picWind);
update();
@@ -624,10 +625,49 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
updateScreen(old_picNotValid);
restoreAndDelete(argc, argv);
- if (getLastCastCount() > 1)
- _s->_throttleTrigger = true;
+ // We update the screen here as well, some scenes like EQ1 credits run w/o calling kGetEvent thus we wouldn't update
+ // screen at all
+ g_sci->getEventManager()->updateScreen();
_ports->setPort(oldPort);
+
+
+ // Now trigger speed throttler
+ switch (_lastCastData.size()) {
+ case 0:
+ // No entries drawn -> no speed throttler triggering
+ break;
+ case 1: {
+
+ // One entry drawn -> check if that entry was a speed benchmark view, if not enable speed throttler
+ AnimateEntry *onlyCast = &_lastCastData[0];
+ if ((onlyCast->viewId == 0) && (onlyCast->loopNo == 13) && (onlyCast->celNo == 0)) {
+ // this one is used by jones talkie
+ if ((onlyCast->celRect.height() == 8) && (onlyCast->celRect.width() == 8))
+ return;
+ }
+ // first loop and first cel used?
+ if ((onlyCast->loopNo == 0) && (onlyCast->celNo == 0)) {
+ // and that cel has a known speed benchmark resolution
+ int16 onlyHeight = onlyCast->celRect.height();
+ int16 onlyWidth = onlyCast->celRect.width();
+ if (((onlyWidth == 12) && (onlyHeight == 35)) || // regular benchmark view ("fred", "Speedy", "ego")
+ ((onlyWidth == 29) && (onlyHeight == 45)) || // King's Quest 5 french "fred"
+ ((onlyWidth == 1) && (onlyHeight == 1))) { // Laura Bow 2 Talkie
+ // check further that there is only one cel in that view
+ GfxView *onlyView = _cache->getView(onlyCast->viewId);
+ if ((onlyView->getLoopCount() == 1) && (onlyView->getCelCount(0)))
+ return;
+ }
+ }
+ _s->_throttleTrigger = true;
+ break;
+ }
+ default:
+ // More than 1 entry drawn -> time for speed throttling
+ _s->_throttleTrigger = true;
+ break;
+ }
}
void GfxAnimate::addToPicSetPicNotValid() {
@@ -652,9 +692,9 @@ void GfxAnimate::kernelAddToPicList(reg_t listReference, int argc, reg_t *argv)
addToPicSetPicNotValid();
}
-void GfxAnimate::kernelAddToPicView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control) {
+void GfxAnimate::kernelAddToPicView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y, int16 priority, int16 control) {
_ports->setPort((Port *)_ports->_picWind);
- addToPicDrawView(viewId, loopNo, celNo, leftPos, topPos, priority, control);
+ addToPicDrawView(viewId, loopNo, celNo, x, y, priority, control);
addToPicSetPicNotValid();
}
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index 2cc59b1767..7e82187eed 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -40,7 +40,7 @@ enum ViewSignals {
kSignalAlwaysUpdate = 0x0020,
kSignalForceUpdate = 0x0040,
kSignalRemoveView = 0x0080,
- kSignalFrozen = 0x0100,
+ kSignalFrozen = 0x0100, // I got frozen today!!
kSignalExtraActor = 0x0200, // unused by us, defines all actors that may be included into the background if speed is too slow
kSignalHitObstacle = 0x0400, // used in the actor movement code by kDoBresen()
kSignalDoesntTurn = 0x0800, // used by _k_dirloop() to determine if an actor can turn or not
@@ -51,12 +51,13 @@ enum ViewSignals {
};
enum ViewScaleSignals {
- kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
- kScaleSignalUnknown1 = 0x0002, // seems to do something with globalvar 2, sets scaleX/scaleY
- kScaleSignalUnknown2 = 0x0004 // really unknown
+ kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
+ kScaleSignalGlobalScaling = 0x0002, // means that global scaling shall get applied on that cel (sets scaleX/scaleY)
+ kScaleSignalUnknown2 = 0x0004 // really unknown
};
struct AnimateEntry {
+ int16 givenOrderNo;
reg_t object;
GuiResourceId viewId;
int16 loopNo;
@@ -72,7 +73,8 @@ struct AnimateEntry {
bool showBitsFlag;
reg_t castHandle;
};
-typedef Common::List<AnimateEntry *> AnimateList;
+typedef Common::List<AnimateEntry> AnimateList;
+typedef Common::Array<AnimateEntry> AnimateArray;
class GfxCache;
class GfxCursor;
@@ -89,9 +91,6 @@ public:
GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions);
virtual ~GfxAnimate();
- // FIXME: Don't store EngineState
- void resetEngineState(EngineState *newState) { _s = newState; }
-
void disposeLastCast();
bool invoke(List *list, int argc, reg_t *argv);
void makeSortedList(List *list);
@@ -104,8 +103,6 @@ public:
void addToPicDrawCels();
void addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control);
- uint16 getLastCastCount() { return _lastCastCount; }
-
virtual void kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv);
virtual void kernelAddToPicList(reg_t listReference, int argc, reg_t *argv);
virtual void kernelAddToPicView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control);
@@ -125,12 +122,8 @@ private:
GfxCursor *_cursor;
GfxTransitions *_transitions;
- uint16 _listCount;
- AnimateEntry *_listData;
AnimateList _list;
-
- uint16 _lastCastCount;
- AnimateEntry *_lastCastData;
+ AnimateArray _lastCastData;
bool _ignoreFastCast;
};
diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp
index 81bdab80ea..25475c727f 100644
--- a/engines/sci/graphics/cache.cpp
+++ b/engines/sci/graphics/cache.cpp
@@ -90,11 +90,11 @@ GfxView *GfxCache::getView(GuiResourceId viewId) {
}
int16 GfxCache::kernelViewGetCelWidth(GuiResourceId viewId, int16 loopNo, int16 celNo) {
- return getView(viewId)->getCelInfo(loopNo, celNo)->width;
+ return getView(viewId)->getCelInfo(loopNo, celNo)->scriptWidth;
}
int16 GfxCache::kernelViewGetCelHeight(GuiResourceId viewId, int16 loopNo, int16 celNo) {
- return getView(viewId)->getCelInfo(loopNo, celNo)->height;
+ return getView(viewId)->getCelInfo(loopNo, celNo)->scriptHeight;
}
int16 GfxCache::kernelViewGetLoopCount(GuiResourceId viewId) {
@@ -102,7 +102,7 @@ int16 GfxCache::kernelViewGetLoopCount(GuiResourceId viewId) {
}
int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) {
- return getView(viewId)->getLoopInfo(loopNo)->celCount;
+ return getView(viewId)->getCelCount(loopNo);
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/cache.h b/engines/sci/graphics/cache.h
index 16ab1916d4..2e9a345230 100644
--- a/engines/sci/graphics/cache.h
+++ b/engines/sci/graphics/cache.h
@@ -26,8 +26,6 @@
#ifndef SCI_GRAPHICS_CACHE_H
#define SCI_GRAPHICS_CACHE_H
-#include "sci/graphics/gui.h"
-
#include "common/hashmap.h"
namespace Sci {
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 36dd2d4aed..1c961b2ad6 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -28,6 +28,7 @@
#include "graphics/primitives.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/graphics/compare.h"
@@ -69,7 +70,7 @@ uint16 GfxCompare::isOnControl(uint16 screenMask, const Common::Rect &rect) {
return result;
}
-bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list) {
+reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list) {
reg_t curAddress = list->first;
Node *curNode = _segMan->lookupNode(curAddress);
reg_t curObject;
@@ -79,12 +80,12 @@ bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &c
while (curNode) {
curObject = curNode->value;
if (curObject != checkObject) {
- signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal));
+ signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) {
- curRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft));
- curRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop));
- curRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight));
- curRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom));
+ curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft));
+ curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop));
+ curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight));
+ curRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom));
// Check if curRect is within checkRect
// TODO: This check is slightly odd, because it means that a rect is not contained
// in itself. It may very well be that the original SCI engine did it just
@@ -95,14 +96,14 @@ bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &c
curRect.left < checkRect.right &&
curRect.bottom > checkRect.top &&
curRect.top < checkRect.bottom) {
- return false;
+ return curObject;
}
}
}
curAddress = curNode->succ;
curNode = _segMan->lookupNode(curAddress);
}
- return true;
+ return NULL_REG;
}
uint16 GfxCompare::kernelOnControl(byte screenMask, const Common::Rect &rect) {
@@ -115,98 +116,150 @@ uint16 GfxCompare::kernelOnControl(byte screenMask, const Common::Rect &rect) {
void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
GfxView *view = NULL;
Common::Rect celRect(0, 0);
- GuiResourceId viewId = (GuiResourceId)GET_SEL32V(_segMan, objectReference, SELECTOR(view));
+ GuiResourceId viewId = (GuiResourceId)readSelectorValue(_segMan, objectReference, SELECTOR(view));
// HACK: Ignore invalid views for now (perhaps unimplemented text views?)
if (viewId == 0xFFFF) // invalid view
return;
- int16 loopNo = GET_SEL32V(_segMan, objectReference, SELECTOR(loop));
- int16 celNo = GET_SEL32V(_segMan, objectReference, SELECTOR(cel));
- int16 x = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(x));
- int16 y = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(y));
+ int16 loopNo = readSelectorValue(_segMan, objectReference, SELECTOR(loop));
+ int16 celNo = readSelectorValue(_segMan, objectReference, SELECTOR(cel));
+ int16 x = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(x));
+ int16 y = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(y));
int16 z = 0;
- if (_kernel->_selectorCache.z > -1)
- z = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(z));
+ if (SELECTOR(z) > -1)
+ z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z));
- // now get cel rectangle
view = _cache->getView(viewId);
- view->getCelRect(loopNo, celNo, x, y, z, &celRect);
- if (lookup_selector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) {
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsLeft), celRect.left);
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsRight), celRect.right);
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsTop), celRect.top);
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom);
+#ifdef ENABLE_SCI32
+ switch (getSciVersion()) {
+ case SCI_VERSION_2:
+ if (view->isSci2Hires())
+ _screen->adjustToUpscaledCoordinates(y, x);
+ break;
+ case SCI_VERSION_2_1:
+ _coordAdjuster->fromScriptToDisplay(y, x);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ view->getCelRect(loopNo, celNo, x, y, z, celRect);
+
+#ifdef ENABLE_SCI32
+ switch (getSciVersion()) {
+ case SCI_VERSION_2:
+ if (view->isSci2Hires()) {
+ _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
+ _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ }
+ break;
+ case SCI_VERSION_2_1: {
+ _coordAdjuster->fromDisplayToScript(celRect.top, celRect.left);
+ _coordAdjuster->fromDisplayToScript(celRect.bottom, celRect.right);
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+
+ if (lookupSelector(_segMan, objectReference, SELECTOR(nsTop), NULL, NULL) == kSelectorVariable) {
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsLeft), celRect.left);
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsRight), celRect.right);
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsTop), celRect.top);
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom);
}
}
-bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) {
+reg_t GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) {
Common::Rect checkRect;
Common::Rect adjustedRect;
uint16 signal, controlMask;
- bool result;
+ uint16 result;
- checkRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft));
- checkRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop));
- checkRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight));
- checkRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom));
+ checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft));
+ checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop));
+ checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight));
+ checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom));
if (!checkRect.isValidRect()) { // can occur in Iceman - HACK? TODO: is this really occuring in sierra sci? check this
warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom);
- return false;
+ return NULL_REG;
}
adjustedRect = _coordAdjuster->onControl(checkRect);
- signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal));
- controlMask = GET_SEL32V(_segMan, curObject, SELECTOR(illegalBits));
- result = (isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask) ? false : true;
- if ((result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) {
+ signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
+ controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits));
+ result = isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask;
+ if ((!result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) {
List *list = _segMan->lookupList(listReference);
if (!list)
error("kCanBeHere called with non-list as parameter");
- result = canBeHereCheckRectList(curObject, checkRect, list);
+ return canBeHereCheckRectList(curObject, checkRect, list);
}
- return result;
+ return make_reg(0, result);
}
bool GfxCompare::kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position) {
GfxView *tmpView = _cache->getView(viewId);
- CelInfo *celInfo = tmpView->getCelInfo(loopNo, celNo);
+ const CelInfo *celInfo = tmpView->getCelInfo(loopNo, celNo);
position.x = CLIP<int>(position.x, 0, celInfo->width - 1);
position.y = CLIP<int>(position.y, 0, celInfo->height - 1);
- byte *celData = tmpView->getBitmap(loopNo, celNo);
+ const byte *celData = tmpView->getBitmap(loopNo, celNo);
bool result = (celData[position.y * celInfo->width + position.x] == celInfo->clearKey);
return result;
}
void GfxCompare::kernelBaseSetter(reg_t object) {
- if (lookup_selector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) {
- int16 x = GET_SEL32V(_segMan, object, SELECTOR(x));
- int16 y = GET_SEL32V(_segMan, object, SELECTOR(y));
- int16 z = (_kernel->_selectorCache.z > -1) ? GET_SEL32V(_segMan, object, SELECTOR(z)) : 0;
- int16 yStep = GET_SEL32V(_segMan, object, SELECTOR(yStep));
- GuiResourceId viewId = GET_SEL32V(_segMan, object, SELECTOR(view));
- int16 loopNo = GET_SEL32V(_segMan, object, SELECTOR(loop));
- int16 celNo = GET_SEL32V(_segMan, object, SELECTOR(cel));
+ if (lookupSelector(_segMan, object, SELECTOR(brLeft), NULL, NULL) == kSelectorVariable) {
+ int16 x = readSelectorValue(_segMan, object, SELECTOR(x));
+ int16 y = readSelectorValue(_segMan, object, SELECTOR(y));
+ int16 z = (SELECTOR(z) > -1) ? readSelectorValue(_segMan, object, SELECTOR(z)) : 0;
+ int16 yStep = readSelectorValue(_segMan, object, SELECTOR(yStep));
+ GuiResourceId viewId = readSelectorValue(_segMan, object, SELECTOR(view));
+ int16 loopNo = readSelectorValue(_segMan, object, SELECTOR(loop));
+ int16 celNo = readSelectorValue(_segMan, object, SELECTOR(cel));
// HACK: Ignore invalid views for now (perhaps unimplemented text views?)
if (viewId == 0xFFFF) // invalid view
return;
- GfxView *tmpView = _cache->getView(viewId);
+ uint16 scaleSignal = 0;
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal));
+ }
+
Common::Rect celRect;
- tmpView->getCelRect(loopNo, celNo, x, y, z, &celRect);
+ GfxView *tmpView = _cache->getView(viewId);
+ if (tmpView->isSci2Hires())
+ _screen->adjustToUpscaledCoordinates(y, x);
+
+ if (scaleSignal & kScaleSignalDoScaling) {
+ int16 scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX));
+ int16 scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY));
+ tmpView->getCelScaledRect(loopNo, celNo, x, y, z, scaleX, scaleY, celRect);
+ } else {
+ tmpView->getCelRect(loopNo, celNo, x, y, z, celRect);
+ }
+
+ if (tmpView->isSci2Hires()) {
+ _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
+ _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ }
+
celRect.bottom = y + 1;
celRect.top = celRect.bottom - yStep;
- PUT_SEL32V(_segMan, object, SELECTOR(brLeft), celRect.left);
- PUT_SEL32V(_segMan, object, SELECTOR(brRight), celRect.right);
- PUT_SEL32V(_segMan, object, SELECTOR(brTop), celRect.top);
- PUT_SEL32V(_segMan, object, SELECTOR(brBottom), celRect.bottom);
+ writeSelectorValue(_segMan, object, SELECTOR(brLeft), celRect.left);
+ writeSelectorValue(_segMan, object, SELECTOR(brRight), celRect.right);
+ writeSelectorValue(_segMan, object, SELECTOR(brTop), celRect.top);
+ writeSelectorValue(_segMan, object, SELECTOR(brBottom), celRect.bottom);
}
}
diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h
index 348d5ef723..1079f5f98c 100644
--- a/engines/sci/graphics/compare.h
+++ b/engines/sci/graphics/compare.h
@@ -26,8 +26,6 @@
#ifndef SCI_GRAPHICS_GFX_H
#define SCI_GRAPHICS_GFX_H
-#include "sci/graphics/gui.h"
-
#include "common/hashmap.h"
namespace Sci {
@@ -44,7 +42,7 @@ public:
uint16 kernelOnControl(byte screenMask, const Common::Rect &rect);
void kernelSetNowSeen(reg_t objectReference);
- bool kernelCanBeHere(reg_t curObject, reg_t listReference);
+ reg_t kernelCanBeHere(reg_t curObject, reg_t listReference);
bool kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position);
void kernelBaseSetter(reg_t object);
@@ -62,7 +60,7 @@ private:
* *different* from checkObject, has a brRect which is contained inside
* checkRect.
*/
- bool canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list);
+ reg_t canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list);
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp
index f744d6e7f1..5891413be8 100644
--- a/engines/sci/graphics/controls.cpp
+++ b/engines/sci/graphics/controls.cpp
@@ -30,6 +30,7 @@
#include "sci/sci.h"
#include "sci/event.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/graphics/ports.h"
@@ -74,7 +75,9 @@ void GfxControls::drawListControl(Common::Rect rect, reg_t obj, int16 maxChars,
// draw UP/DOWN arrows
// we draw UP arrow one pixel lower than sierra did, because it looks nicer. Also the DOWN arrow has one pixel
// line inbetween as well
- workerRect.top++;
+ // They "fixed" this in SQ4 by having the arrow character start one pixel line later, we don't adjust there
+ if (g_sci->getGameId() != GID_SQ4)
+ workerRect.top++;
_text16->Box(controlListUpArrow, 0, workerRect, SCI_TEXT16_ALIGNMENT_CENTER, 0);
workerRect.top = workerRect.bottom - 10;
_text16->Box(controlListDownArrow, 0, workerRect, SCI_TEXT16_ALIGNMENT_CENTER, 0);
@@ -88,7 +91,7 @@ void GfxControls::drawListControl(Common::Rect rect, reg_t obj, int16 maxChars,
_text16->SetFont(fontId);
fontSize = _ports->_curPort->fontHeight;
_ports->penColor(_ports->_curPort->penClr); _ports->backColor(_ports->_curPort->backClr);
- workerRect.bottom = workerRect.top + 9;
+ workerRect.bottom = workerRect.top + fontSize;
lastYpos = rect.bottom - fontSize;
// Write actual text
@@ -143,9 +146,9 @@ void GfxControls::texteditSetBlinkTime() {
}
void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
- uint16 cursorPos = GET_SEL32V(_segMan, controlObject, SELECTOR(cursor));
- uint16 maxChars = GET_SEL32V(_segMan, controlObject, SELECTOR(max));
- reg_t textReference = GET_SEL32(_segMan, controlObject, SELECTOR(text));
+ uint16 cursorPos = readSelectorValue(_segMan, controlObject, SELECTOR(cursor));
+ uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max));
+ reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
Common::String text;
uint16 textSize, eventType, eventKey = 0;
bool textChanged = false;
@@ -158,14 +161,14 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
if (!eventObject.isNull()) {
textSize = text.size();
- eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type));
+ eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
switch (eventType) {
case SCI_EVENT_MOUSE_PRESS:
// TODO: Implement mouse support for cursor change
break;
case SCI_EVENT_KEYBOARD:
- eventKey = GET_SEL32V(_segMan, eventObject, SELECTOR(message));
+ eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message));
switch (eventKey) {
case SCI_KEY_BACKSPACE:
if (cursorPos > 0) {
@@ -207,9 +210,9 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
if (textChanged) {
GuiResourceId oldFontId = _text16->GetFontId();
- GuiResourceId fontId = GET_SEL32V(_segMan, controlObject, SELECTOR(font));
- rect = Common::Rect(GET_SEL32V(_segMan, controlObject, SELECTOR(nsLeft)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsTop)),
- GET_SEL32V(_segMan, controlObject, SELECTOR(nsRight)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsBottom)));
+ GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
+ rect = Common::Rect(readSelectorValue(_segMan, controlObject, SELECTOR(nsLeft)), readSelectorValue(_segMan, controlObject, SELECTOR(nsTop)),
+ readSelectorValue(_segMan, controlObject, SELECTOR(nsRight)), readSelectorValue(_segMan, controlObject, SELECTOR(nsBottom)));
_text16->SetFont(fontId);
if (textAddChar) {
// We check, if we are really able to add the new char
@@ -241,7 +244,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
}
}
- PUT_SEL32V(_segMan, controlObject, SELECTOR(cursor), cursorPos);
+ writeSelectorValue(_segMan, controlObject, SELECTOR(cursor), cursorPos);
}
int GfxControls::getPicNotValid() {
diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp
index 40ef655be7..bbeade87b5 100644
--- a/engines/sci/graphics/coordadjuster.cpp
+++ b/engines/sci/graphics/coordadjuster.cpp
@@ -26,10 +26,12 @@
#include "common/util.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/ports.h"
+#include "sci/graphics/screen.h"
namespace Sci {
@@ -87,32 +89,41 @@ Common::Rect GfxCoordAdjuster16::pictureGetDisplayArea() {
#ifdef ENABLE_SCI32
GfxCoordAdjuster32::GfxCoordAdjuster32(SegManager *segMan)
: _segMan(segMan) {
+ scriptsRunningWidth = 0;
+ scriptsRunningHeight = 0;
}
GfxCoordAdjuster32::~GfxCoordAdjuster32() {
}
void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) {
- //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY));
- //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX));
- //*x = ( *x * _screen->getWidth()) / resX;
- //*y = ( *y * _screen->getHeight()) / resY;
- x -= GET_SEL32V(_segMan, planeObject, SELECTOR(left));
- y -= GET_SEL32V(_segMan, planeObject, SELECTOR(top));
+ uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top));
+ uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left));
+
+ y -= planeTop;
+ x -= planeLeft;
}
void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) {
- //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY));
- //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX));
- x += GET_SEL32V(_segMan, planeObject, SELECTOR(left));
- y += GET_SEL32V(_segMan, planeObject, SELECTOR(top));
- //*x = ( *x * resX) / _screen->getWidth();
- //*y = ( *y * resY) / _screen->getHeight();
+ uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top));
+ uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left));
+
+ x += planeLeft;
+ y += planeTop;
}
-Common::Rect GfxCoordAdjuster32::onControl(Common::Rect rect) {
- Common::Rect adjustedRect = rect;
- adjustedRect.translate(0, 10);
- return adjustedRect;
+void GfxCoordAdjuster32::setScriptsResolution(uint16 width, uint16 height) {
+ scriptsRunningWidth = width;
+ scriptsRunningHeight = height;
+}
+
+void GfxCoordAdjuster32::fromDisplayToScript(int16 &y, int16 &x) {
+ y = ((y * scriptsRunningHeight) / g_sci->_gfxScreen->getHeight());
+ x = ((x * scriptsRunningWidth) / g_sci->_gfxScreen->getWidth());
+}
+
+void GfxCoordAdjuster32::fromScriptToDisplay(int16 &y, int16 &x) {
+ y = ((y * g_sci->_gfxScreen->getHeight()) / scriptsRunningHeight);
+ x = ((x * g_sci->_gfxScreen->getWidth()) / scriptsRunningWidth);
}
void GfxCoordAdjuster32::pictureSetDisplayArea(Common::Rect displayArea) {
diff --git a/engines/sci/graphics/coordadjuster.h b/engines/sci/graphics/coordadjuster.h
index 9b2bef48f1..59afd1dcb7 100644
--- a/engines/sci/graphics/coordadjuster.h
+++ b/engines/sci/graphics/coordadjuster.h
@@ -50,6 +50,10 @@ public:
virtual void setCursorPos(Common::Point &pos) { }
virtual void moveCursor(Common::Point &pos) { }
+ virtual void setScriptsResolution(uint16 width, uint16 height) { }
+ virtual void fromScriptToDisplay(int16 &y, int16 &x) { }
+ virtual void fromDisplayToScript(int16 &y, int16 &x) { }
+
virtual Common::Rect pictureGetDisplayArea() { return Common::Rect(0, 0); }
private:
};
@@ -83,7 +87,9 @@ public:
void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG);
void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG);
- Common::Rect onControl(Common::Rect rect);
+ void setScriptsResolution(uint16 width, uint16 height);
+ void fromScriptToDisplay(int16 &y, int16 &x);
+ void fromDisplayToScript(int16 &y, int16 &x);
void pictureSetDisplayArea(Common::Rect displayArea);
Common::Rect pictureGetDisplayArea();
@@ -92,6 +98,9 @@ private:
SegManager *_segMan;
Common::Rect _pictureDisplayArea;
+
+ uint16 scriptsRunningWidth;
+ uint16 scriptsRunningHeight;
};
#endif
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 2f8393f9ac..f6e2077cb3 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -43,18 +43,18 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc
: _resMan(resMan), _palette(palette), _screen(screen) {
_upscaledHires = _screen->getUpscaledHires();
+ _isVisible = true;
+
// center mouse cursor
setPosition(Common::Point(_screen->getWidth() / 2, _screen->getHeight() / 2));
- kernelSetMoveZone(Common::Rect(0, 0, _screen->getDisplayWidth(), _screen->getDisplayHeight()));
-
- _isVisible = true;
+ _moveZoneActive = false;
}
GfxCursor::~GfxCursor() {
purgeCache();
}
-void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, SciEvent *event) {
+void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, EventManager *event) {
_coordAdjuster = coordAdjuster;
_event = event;
}
@@ -120,7 +120,7 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
colorMapping[0] = 0; // Black is hardcoded
colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded
colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR;
- colorMapping[3] = _palette->matchColor(&_palette->_sysPalette, 170, 170, 170); // Grey
+ colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey
// Seek to actual data
resourceData += 4;
@@ -164,42 +164,39 @@ void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Co
GfxView *cursorView = _cachedCursors[viewNum];
- CelInfo *celInfo = cursorView->getCelInfo(loopNum, celNum);
+ const CelInfo *celInfo = cursorView->getCelInfo(loopNum, celNum);
int16 width = celInfo->width;
int16 height = celInfo->height;
byte clearKey = celInfo->clearKey;
Common::Point *cursorHotspot = hotspot;
- byte *cursorBitmap;
if (!cursorHotspot)
// Compute hotspot from xoffset/yoffset
cursorHotspot = new Common::Point((celInfo->width >> 1) - celInfo->displaceX, celInfo->height - celInfo->displaceY - 1);
- // Eco Quest 1 uses a 1x1 transparent cursor to hide the cursor from the user. Some scalers don't seem to support this
+ // Eco Quest 1 uses a 1x1 transparent cursor to hide the cursor from the
+ // user. Some scalers don't seem to support this
if (width < 2 || height < 2) {
kernelHide();
delete cursorHotspot;
return;
}
- celInfo->rawBitmap = cursorView->getBitmap(loopNum, celNum);
+ const byte *rawBitmap = cursorView->getBitmap(loopNum, celNum);
if (_upscaledHires) {
// Scale cursor by 2x - note: sierra didn't do this, but it looks much better
width *= 2;
height *= 2;
cursorHotspot->x *= 2;
cursorHotspot->y *= 2;
- cursorBitmap = new byte[width * height];
- _screen->scale2x(celInfo->rawBitmap, cursorBitmap, celInfo->width, celInfo->height);
+ byte *cursorBitmap = new byte[width * height];
+ _screen->scale2x(rawBitmap, cursorBitmap, celInfo->width, celInfo->height);
+ CursorMan.replaceCursor(cursorBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
+ delete[] cursorBitmap;
} else {
- cursorBitmap = celInfo->rawBitmap;
+ CursorMan.replaceCursor(rawBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
}
- CursorMan.replaceCursor(cursorBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
-
- if (_upscaledHires)
- delete[] cursorBitmap;
-
kernelShow();
delete cursorHotspot;
@@ -209,7 +206,7 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
// See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-402.html
// for more information.
- // View 998 seems to be a fake resource used to call for for the Mac CURS resources
+ // View 998 seems to be a fake resource used to call for the Mac CURS resources.
// For other resources, they're still in the views, so use them.
if (viewNum != 998) {
kernelSetView(viewNum, loopNum, celNum, hotspot);
@@ -219,6 +216,7 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
// TODO: What about the 2000 resources? Inventory items? How to handle?
// TODO: What games does this work for? At least it does for KQ6.
// TODO: Stop asking rhetorical questions.
+ // TODO: It was fred all along!
Resource *resource = _resMan->findResource(ResourceId(kResourceTypeCursor, 1000 + celNum), false);
@@ -258,6 +256,15 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
}
void GfxCursor::setPosition(Common::Point pos) {
+ // Don't set position, when cursor is not visible.
+ // This fixes eco quest 1 (floppy) right at the start, which is setting
+ // mouse cursor to (0,0) all the time during the intro. It's escapeable
+ // (now) by moving to the left or top, but it's getting on your nerves. This
+ // could theoretically break some things, although sierra normally sets
+ // position only when showing the cursor.
+ if (!_isVisible)
+ return;
+
if (!_upscaledHires) {
g_system->warpMouse(pos.x, pos.y);
} else {
@@ -269,52 +276,46 @@ void GfxCursor::setPosition(Common::Point pos) {
Common::Point GfxCursor::getPosition() {
Common::Point mousePos = g_system->getEventManager()->getMousePos();
- switch (_upscaledHires) {
- case GFX_SCREEN_UPSCALED_640x400:
- mousePos.x /= 2;
- mousePos.y /= 2;
- break;
- case GFX_SCREEN_UPSCALED_640x440:
- mousePos.x /= 2;
- mousePos.y = (mousePos.y * 5) / 11;
- break;
- case GFX_SCREEN_UPSCALED_640x480:
- mousePos.x /= 2;
- mousePos.y = (mousePos.y * 5) / 12;
- default:
- break;
- }
+ if (_upscaledHires)
+ _screen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
return mousePos;
}
void GfxCursor::refreshPosition() {
- bool clipped = false;
- Common::Point mousePoint = getPosition();
-
- if (mousePoint.x < _moveZone.left) {
- mousePoint.x = _moveZone.left;
- clipped = true;
- } else if (mousePoint.x >= _moveZone.right) {
- mousePoint.x = _moveZone.right - 1;
- clipped = true;
- }
+ if (_moveZoneActive) {
+ bool clipped = false;
+ Common::Point mousePoint = getPosition();
+
+ if (mousePoint.x < _moveZone.left) {
+ mousePoint.x = _moveZone.left;
+ clipped = true;
+ } else if (mousePoint.x >= _moveZone.right) {
+ mousePoint.x = _moveZone.right - 1;
+ clipped = true;
+ }
+
+ if (mousePoint.y < _moveZone.top) {
+ mousePoint.y = _moveZone.top;
+ clipped = true;
+ } else if (mousePoint.y >= _moveZone.bottom) {
+ mousePoint.y = _moveZone.bottom - 1;
+ clipped = true;
+ }
- if (mousePoint.y < _moveZone.top) {
- mousePoint.y = _moveZone.top;
- clipped = true;
- } else if (mousePoint.y >= _moveZone.bottom) {
- mousePoint.y = _moveZone.bottom - 1;
- clipped = true;
+ // FIXME: Do this only when mouse is grabbed?
+ if (clipped)
+ setPosition(mousePoint);
}
+}
- // FIXME: Do this only when mouse is grabbed?
- if (clipped)
- setPosition(mousePoint);
+void GfxCursor::kernelResetMoveZone() {
+ _moveZoneActive = false;
}
void GfxCursor::kernelSetMoveZone(Common::Rect zone) {
- _moveZone = zone;
+ _moveZone = zone;
+ _moveZoneActive = true;
}
void GfxCursor::kernelSetPos(Common::Point pos) {
@@ -333,7 +334,7 @@ void GfxCursor::kernelMoveCursor(Common::Point pos) {
// Trigger event reading to make sure the mouse coordinates will
// actually have changed the next time we read them.
- _event->get(SCI_EVENT_PEEK);
+ _event->getSciEvent(SCI_EVENT_PEEK);
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index 6d92b3cf5f..787841f5be 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -45,7 +45,7 @@ public:
GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *screen);
~GfxCursor();
- void init(GfxCoordAdjuster *coordAdjuster, SciEvent *event);
+ void init(GfxCoordAdjuster *coordAdjuster, EventManager *event);
void kernelShow();
void kernelHide();
@@ -58,6 +58,11 @@ public:
void refreshPosition();
/**
+ * Removes limit for mouse movement
+ */
+ void kernelResetMoveZone();
+
+ /**
* Limits the mouse movement to a given rectangle.
*
* @param[in] rect The rectangle
@@ -74,10 +79,11 @@ private:
GfxScreen *_screen;
GfxPalette *_palette;
GfxCoordAdjuster *_coordAdjuster;
- SciEvent *_event;
+ EventManager *_event;
int _upscaledHires;
+ bool _moveZoneActive;
Common::Rect _moveZone; // Rectangle in which the pointer can move
CursorCache _cachedCursors;
diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp
index 91cf01c912..852771d081 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -82,17 +82,18 @@ void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bo
int charWidth = MIN<int>(getCharWidth(chr), _screen->getWidth() - left);
int charHeight = MIN<int>(getCharHeight(chr), _screen->getHeight() - top);
byte b = 0, mask = 0xFF;
- int y = top;
+ int y = 0;
+ int16 greyedTop = top;
byte *pIn = getCharData(chr);
for (int i = 0; i < charHeight; i++, y++) {
if (greyedOutput)
- mask = top++ % 2 ? 0xAA : 0x55;
+ mask = greyedTop++ % 2 ? 0xAA : 0x55;
for (int done = 0; done < charWidth; done++) {
if ((done & 7) == 0) // fetching next data byte
b = *(pIn++) & mask;
if (b & 0x80) // if MSB is set - paint it
- _screen->putPixel(left + done, y, 1, color, 0, 0);
+ _screen->putFontPixel(top, left + done, y, color);
b = b << 1;
}
}
diff --git a/engines/sci/graphics/font.h b/engines/sci/graphics/font.h
index 90f18e426d..b9bee0fa9e 100644
--- a/engines/sci/graphics/font.h
+++ b/engines/sci/graphics/font.h
@@ -32,14 +32,14 @@ namespace Sci {
class GfxFont {
public:
- GfxFont() {};
- virtual ~GfxFont() {};
-
- virtual GuiResourceId getResourceId() { return 0; };
- virtual byte getHeight() { return 0; };
- virtual bool isDoubleByte(uint16 chr) { return false; };
- virtual byte getCharWidth(uint16 chr) { return 0; };
- virtual void draw(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput) {};
+ GfxFont() {}
+ virtual ~GfxFont() {}
+
+ virtual GuiResourceId getResourceId() { return 0; }
+ virtual byte getHeight() { return 0; }
+ virtual bool isDoubleByte(uint16 chr) { return false; }
+ virtual byte getCharWidth(uint16 chr) { return 0; }
+ virtual void draw(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput) {}
};
diff --git a/engines/sci/graphics/fontsjis.h b/engines/sci/graphics/fontsjis.h
index 24c8423ddb..684e6cac5e 100644
--- a/engines/sci/graphics/fontsjis.h
+++ b/engines/sci/graphics/fontsjis.h
@@ -26,10 +26,12 @@
#ifndef SCI_GRAPHICS_FONTSJIS_H
#define SCI_GRAPHICS_FONTSJIS_H
-#include "graphics/sjis.h"
-
#include "sci/graphics/helpers.h"
+namespace Graphics {
+ class FontSJIS;
+}
+
namespace Sci {
/**
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 78253bd913..a433b26ef2 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -28,6 +28,7 @@
#include "graphics/primitives.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -37,6 +38,7 @@
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint32.h"
+#include "sci/graphics/palette.h"
#include "sci/graphics/picture.h"
#include "sci/graphics/frameout.h"
@@ -46,43 +48,103 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd
: _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32) {
_coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
- _highPlanePri = 0;
+ scriptsRunningWidth = 320;
+ scriptsRunningHeight = 200;
}
GfxFrameout::~GfxFrameout() {
}
void GfxFrameout::kernelAddPlane(reg_t object) {
- _planes.push_back(object);
- int16 planePri = GET_SEL32V(_segMan, object, SELECTOR(priority)) & 0xFFFF;
- if (planePri > _highPlanePri)
- _highPlanePri = planePri;
+ PlaneEntry newPlane;
+
+ if (_planes.empty()) {
+ // There has to be another way for sierra sci to do this or maybe script resolution is compiled into
+ // interpreter (TODO)
+ scriptsRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
+ scriptsRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
+ _coordAdjuster->setScriptsResolution(scriptsRunningWidth, scriptsRunningHeight);
+ }
+
+ newPlane.object = object;
+ newPlane.pictureId = 0xFFFF;
+ newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority));
+ newPlane.lastPriority = 0xFFFF; // hidden
+ _planes.push_back(newPlane);
+
+ kernelUpdatePlane(object);
}
void GfxFrameout::kernelUpdatePlane(reg_t object) {
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
+ if (it->object == object) {
+ // Read some information
+ it->priority = readSelectorValue(_segMan, object, SELECTOR(priority));
+ GuiResourceId lastPictureId = it->pictureId;
+ it->pictureId = readSelectorValue(_segMan, object, SELECTOR(picture));
+ if (lastPictureId != it->pictureId) {
+ // picture got changed, load new picture
+ deletePlanePictures(object);
+ if ((it->pictureId != 0xFFFF) && (it->pictureId != 0xFFFE)) {
+ // SQ6 gives us a bad picture number for the control menu
+ if (_resMan->testResource(ResourceId(kResourceTypePic, it->pictureId)))
+ addPlanePicture(object, it->pictureId, 0);
+ }
+ }
+ sortPlanes();
+ return;
+ }
+ }
+ error("kUpdatePlane called on plane that wasn't added before");
}
void GfxFrameout::kernelDeletePlane(reg_t object) {
- for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
- if (_planes[planeNr] == object) {
- _planes.remove_at(planeNr);
- break;
+ deletePlanePictures(object);
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
+ if (it->object == object) {
+ _planes.erase(it);
+ Common::Rect planeRect;
+ planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top));
+ planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left));
+ planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom)) + 1;
+ planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right)) + 1;
+
+ Common::Rect screenRect(_screen->getWidth(), _screen->getHeight());
+ planeRect.top = (planeRect.top * screenRect.height()) / scriptsRunningHeight;
+ planeRect.left = (planeRect.left * screenRect.width()) / scriptsRunningWidth;
+ planeRect.bottom = (planeRect.bottom * screenRect.height()) / scriptsRunningHeight;
+ planeRect.right = (planeRect.right * screenRect.width()) / scriptsRunningWidth;
+ planeRect.clip(screenRect); // we need to do this, at least in gk1 on cemetary we get bottom right -> 201, 321
+ // Blackout removed plane rect
+ _paint32->fillRect(planeRect, 0);
+ return;
}
}
+}
- // Recalculate highPlanePri
- _highPlanePri = 0;
+void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX) {
+ PlanePictureEntry newPicture;
+ newPicture.object = object;
+ newPicture.pictureId = pictureId;
+ newPicture.picture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, pictureId, false);
+ newPicture.startX = startX;
+ newPicture.pictureCels = 0;
+ _planePictures.push_back(newPicture);
+}
- for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
- int16 planePri = GET_SEL32V(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF;
- if (planePri > _highPlanePri)
- _highPlanePri = planePri;
+void GfxFrameout::deletePlanePictures(reg_t object) {
+ for (PlanePictureList::iterator it = _planePictures.begin(); it != _planePictures.end(); it++) {
+ if (it->object == object) {
+ delete it->picture;
+ _planePictures.erase(it);
+ deletePlanePictures(object);
+ return;
+ }
}
}
void GfxFrameout::kernelAddScreenItem(reg_t object) {
_screenItems.push_back(object);
- warning("addScreenItem %X:%X (%s)", object.segment, object.offset, _segMan->getObjectName(object));
}
void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
@@ -95,186 +157,378 @@ void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
}
int16 GfxFrameout::kernelGetHighPlanePri() {
- return _highPlanePri;
+ sortPlanes();
+ return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back().object, SELECTOR(priority));
+}
+
+// No idea yet how to implement this
+void GfxFrameout::kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId) {
+ addPlanePicture(planeObj, pictureId, forWidth);
}
bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) {
- return (entry1->priority == entry2->priority) ? (entry1->y < entry2->y) : (entry1->priority < entry2->priority);
+ if (entry1->priority == entry2->priority) {
+ if (entry1->y == entry2->y)
+ return (entry1->givenOrderNr < entry2->givenOrderNr);
+ return (entry1->y < entry2->y);
+ }
+ return (entry1->priority < entry2->priority);
+}
+
+bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) {
+// SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+// uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority));
+// uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority));
+
+ if (entry1.priority == 0xffff)
+ return true;
+
+ if (entry2.priority == 0xffff)
+ return false;
+
+ return entry1.priority < entry2.priority;
+}
+
+void GfxFrameout::sortPlanes() {
+ // First, remove any invalid planes
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end();) {
+ if (!_segMan->isObject(it->object))
+ it = _planes.erase(it);
+ else
+ it++;
+ }
+
+ // Sort the rest of them
+ Common::sort(_planes.begin(), _planes.end(), planeSortHelper);
}
void GfxFrameout::kernelFrameout() {
- int16 itemCount = 0;
- reg_t planeObject;
- GuiResourceId planePictureNr;
- GfxPicture *planePicture = 0;
- int16 planePictureCels = 0;
- int16 planePictureCel;
- int16 planePriority;
- Common::Rect planeRect;
- int16 planeResY, planeResX;
- byte planeBack;
-
- reg_t itemObject;
- reg_t itemPlane;
-
- FrameoutEntry *itemData;
- FrameoutList itemList;
- FrameoutEntry *itemEntry;
+ _palette->palVaryUpdate();
// Allocate enough space for all screen items
- itemData = (FrameoutEntry *)malloc(_screenItems.size() * sizeof(FrameoutEntry));
+ FrameoutEntry *itemData = new FrameoutEntry[_screenItems.size()];
- for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
- planeObject = _planes[planeNr];
- planePriority = GET_SEL32V(_segMan, planeObject, SELECTOR(priority));
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
+ reg_t planeObject = it->object;
+ uint16 planeLastPriority = it->lastPriority;
- if (planePriority == -1) // Plane currently not meant to be shown
- continue;
+ Common::Rect planeRect;
+ planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top));
+ planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left));
+ planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)) + 1;
+ planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)) + 1;
- planeRect.top = GET_SEL32V(_segMan, planeObject, SELECTOR(top));
- planeRect.left = GET_SEL32V(_segMan, planeObject, SELECTOR(left));
- planeRect.bottom = GET_SEL32V(_segMan, planeObject, SELECTOR(bottom));
- planeRect.right = GET_SEL32V(_segMan, planeObject, SELECTOR(right));
- planeResY = GET_SEL32V(_segMan, planeObject, SELECTOR(resY));
- planeResX = GET_SEL32V(_segMan, planeObject, SELECTOR(resX));
+ // Update priority here, sq6 sets it w/o UpdatePlane
+ uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
- planeRect.top = (planeRect.top * _screen->getHeight()) / planeResY;
- planeRect.left = (planeRect.left * _screen->getWidth()) / planeResX;
- planeRect.bottom = (planeRect.bottom * _screen->getHeight()) / planeResY;
- planeRect.right = (planeRect.right * _screen->getWidth()) / planeResX;
+ Common::Rect screenRect(_screen->getWidth(), _screen->getHeight());
+ planeRect.top = (planeRect.top * screenRect.height()) / scriptsRunningHeight;
+ planeRect.left = (planeRect.left * screenRect.width()) / scriptsRunningWidth;
+ planeRect.bottom = (planeRect.bottom * screenRect.height()) / scriptsRunningHeight;
+ planeRect.right = (planeRect.right * screenRect.width()) / scriptsRunningWidth;
- planeBack = GET_SEL32V(_segMan, planeObject, SELECTOR(back));
- if (planeBack) {
- _paint32->fillRect(planeRect, planeBack);
+ int16 planeOffsetX = 0;
+
+ // We get negative left in kq7 in scrolling rooms
+ if (planeRect.left < 0) {
+ planeOffsetX = -planeRect.left;
+ planeRect.left = 0;
+ }
+ if (planeRect.top < 0)
+ planeRect.top = 0;
+ // We get bad plane-bottom in sq6
+ if (planeRect.right > _screen->getWidth())
+ planeRect.right = _screen->getWidth();
+ if (planeRect.bottom > _screen->getHeight())
+ planeRect.bottom = _screen->getHeight();
+
+ it->lastPriority = planePriority;
+ if (planePriority == 0xffff) { // Plane currently not meant to be shown
+ // If plane was shown before, delete plane rect
+ if (planePriority != planeLastPriority)
+ _paint32->fillRect(planeRect, 0);
+ continue;
}
- planePictureNr = GET_SEL32V(_segMan, planeObject, SELECTOR(picture));
- if ((planePictureNr != 0xFFFF) && (planePictureNr != 0xFFFE)) {
- planePicture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, planePictureNr, false);
- planePictureCels = planePicture->getSci32celCount();
+ Common::Rect planeClipRect(planeRect.width(), planeRect.height());
- _coordAdjuster->pictureSetDisplayArea(planeRect);
+ Common::Rect upscaledPlaneRect = planeRect;
+ Common::Rect upscaledPlaneClipRect = planeClipRect;
+ if (_screen->getUpscaledHires()) {
+ _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.top, upscaledPlaneRect.left);
+ _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.bottom, upscaledPlaneRect.right);
+ _screen->adjustToUpscaledCoordinates(upscaledPlaneClipRect.top, upscaledPlaneClipRect.left);
+ _screen->adjustToUpscaledCoordinates(upscaledPlaneClipRect.bottom, upscaledPlaneClipRect.right);
}
+ byte planeBack = readSelectorValue(_segMan, planeObject, SELECTOR(back));
+ if (planeBack)
+ _paint32->fillRect(planeRect, planeBack);
+
+ GuiResourceId planeMainPictureId = it->pictureId;
+
+ bool planePictureMirrored = false;
+ if (readSelectorValue(_segMan, planeObject, SELECTOR(mirrored)))
+ planePictureMirrored = true;
+
+ _coordAdjuster->pictureSetDisplayArea(planeRect);
+ _palette->drewPicture(planeMainPictureId);
+
// Fill our itemlist for this plane
- itemCount = 0;
- itemEntry = itemData;
+ int16 itemCount = 0;
+ FrameoutEntry *itemEntry = itemData;
+ FrameoutList itemList;
+
for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
- itemObject = _screenItems[itemNr];
- itemPlane = GET_SEL32(_segMan, itemObject, SELECTOR(plane));
- if (planeObject == itemPlane) {
- // Found an item on current plane
- itemEntry->viewId = GET_SEL32V(_segMan, itemObject, SELECTOR(view));
- itemEntry->loopNo = GET_SEL32V(_segMan, itemObject, SELECTOR(loop));
- itemEntry->celNo = GET_SEL32V(_segMan, itemObject, SELECTOR(cel));
- itemEntry->x = GET_SEL32V(_segMan, itemObject, SELECTOR(x));
- itemEntry->y = GET_SEL32V(_segMan, itemObject, SELECTOR(y));
- itemEntry->z = GET_SEL32V(_segMan, itemObject, SELECTOR(z));
- itemEntry->priority = GET_SEL32V(_segMan, itemObject, SELECTOR(priority));
- itemEntry->signal = GET_SEL32V(_segMan, itemObject, SELECTOR(signal));
- itemEntry->scaleX = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleX));
- itemEntry->scaleY = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleY));
- itemEntry->object = itemObject;
+ reg_t itemObject = _screenItems[itemNr];
- itemEntry->y = ((itemEntry->y * _screen->getHeight()) / planeResY);
- itemEntry->x = ((itemEntry->x * _screen->getWidth()) / planeResX);
- itemEntry->y += planeRect.top;
- itemEntry->x += planeRect.left;
+ // Remove any invalid items
+ if (!_segMan->isObject(itemObject)) {
+ _screenItems.remove_at(itemNr);
+ itemNr--;
+ continue;
+ }
- if (itemEntry->priority == 0)
+ reg_t itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane));
+ if (planeObject == itemPlane) {
+ // Found an item on current plane
+ itemEntry->viewId = readSelectorValue(_segMan, itemObject, SELECTOR(view));
+ itemEntry->loopNo = readSelectorValue(_segMan, itemObject, SELECTOR(loop));
+ itemEntry->celNo = readSelectorValue(_segMan, itemObject, SELECTOR(cel));
+ itemEntry->x = readSelectorValue(_segMan, itemObject, SELECTOR(x));
+ itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y));
+ itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z));
+ itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority));
+ if (readSelectorValue(_segMan, itemObject, SELECTOR(fixPriority)) == 0)
itemEntry->priority = itemEntry->y;
+ itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal));
+ itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX));
+ itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY));
+ itemEntry->object = itemObject;
+ itemEntry->givenOrderNr = itemNr;
+
itemList.push_back(itemEntry);
itemEntry++;
itemCount++;
}
}
+ for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
+ if (pictureIt->object == planeObject) {
+ GfxPicture *planePicture = pictureIt->picture;
+ // Allocate memory for picture cels
+ pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()];
+
+ // Add following cels to the itemlist
+ FrameoutEntry *picEntry = pictureIt->pictureCels;
+ int planePictureCels = planePicture->getSci32celCount();
+ for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) {
+ picEntry->celNo = pictureCelNr;
+ picEntry->object = NULL_REG;
+ picEntry->picture = planePicture;
+ picEntry->y = planePicture->getSci32celY(pictureCelNr);
+ picEntry->x = planePicture->getSci32celX(pictureCelNr);
+ picEntry->picStartX = pictureIt->startX;
+
+ picEntry->priority = planePicture->getSci32celPriority(pictureCelNr);
+
+ itemList.push_back(picEntry);
+ picEntry++;
+ }
+ }
+ }
+
// Now sort our itemlist
Common::sort(itemList.begin(), itemList.end(), sortHelper);
// Now display itemlist
- planePictureCel = 0;
-
itemEntry = itemData;
- FrameoutList::iterator listIterator = itemList.begin();
- FrameoutList::iterator listEnd = itemList.end();
- while (listIterator != listEnd) {
+
+// warning("Plane %s", _segMan->getObjectName(planeObject));
+
+ for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
itemEntry = *listIterator;
- if (planePicture) {
- while ((planePictureCel <= itemEntry->priority) && (planePictureCel < planePictureCels)) {
- planePicture->drawSci32Vga(planePictureCel);
- planePictureCel++;
+
+ if (itemEntry->object.isNull()) {
+ // Picture cel data
+ itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight);
+ itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth);
+ itemEntry->picStartX = ((itemEntry->picStartX * _screen->getWidth()) / scriptsRunningWidth);
+
+ // Out of view
+ int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x;
+ int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo);
+ int16 planeStartX = planeOffsetX;
+ int16 planeEndX = planeStartX + planeRect.width();
+ if (pictureCelEndX < planeStartX)
+ continue;
+ if (pictureCelStartX > planeEndX)
+ continue;
+
+ int16 pictureOffsetX = planeOffsetX;
+ int16 pictureX = itemEntry->x;
+ if ((planeOffsetX) || (itemEntry->picStartX)) {
+ if (planeOffsetX <= itemEntry->picStartX) {
+ pictureX += itemEntry->picStartX - planeOffsetX;
+ pictureOffsetX = 0;
+ } else {
+ pictureOffsetX = planeOffsetX - itemEntry->picStartX;
+ }
}
- }
- if (itemEntry->viewId != 0xFFFF) {
+
+ itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, planePictureMirrored);
+// warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority);
+
+ } else if (itemEntry->viewId != 0xFFFF) {
GfxView *view = _cache->getView(itemEntry->viewId);
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) {
- view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, &itemEntry->celRect);
- } else
- view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, &itemEntry->celRect);
+// warning("view %s %04x:%04x", _segMan->getObjectName(itemEntry->object), PRINT_REG(itemEntry->object));
- if (itemEntry->celRect.top < 0 || itemEntry->celRect.top >= _screen->getHeight()) {
- listIterator++;
- continue;
+ switch (getSciVersion()) {
+ case SCI_VERSION_2:
+ if (view->isSci2Hires()) {
+ int16 dummyX = 0;
+ _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
+ _screen->adjustToUpscaledCoordinates(itemEntry->z, dummyX);
+ }
+ break;
+ case SCI_VERSION_2_1:
+ itemEntry->y = (itemEntry->y * _screen->getHeight()) / scriptsRunningHeight;
+ itemEntry->x = (itemEntry->x * _screen->getWidth()) / scriptsRunningWidth;
+ itemEntry->z = (itemEntry->z * _screen->getHeight()) / scriptsRunningHeight;
+ break;
+ default:
+ break;
+ }
+ // Adjust according to current scroll position
+ itemEntry->x -= planeOffsetX;
+
+ uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect));
+ if (useInsetRect) {
+ itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop));
+ itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft));
+ itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)) + 1;
+ itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)) + 1;
+ if (view->isSci2Hires()) {
+ _screen->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left);
+ _screen->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right);
+ }
+ itemEntry->celRect.translate(itemEntry->x, itemEntry->y);
+ // TODO: maybe we should clip the cels rect with this, i'm not sure
+ // the only currently known usage is game menu of gk1
+ } else {
+ if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
+ view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect);
+ else
+ view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect);
+
+ Common::Rect nsRect = itemEntry->celRect;
+ // Translate back to actual coordinate within scrollable plane
+ nsRect.translate(planeOffsetX, 0);
+ switch (getSciVersion()) {
+ case SCI_VERSION_2:
+ if (view->isSci2Hires()) {
+ _screen->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left);
+ _screen->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right);
+ }
+ break;
+ case SCI_VERSION_2_1:
+ nsRect.top = (nsRect.top * scriptsRunningHeight) / _screen->getHeight();
+ nsRect.left = (nsRect.left * scriptsRunningWidth) / _screen->getWidth();
+ nsRect.bottom = (nsRect.bottom * scriptsRunningHeight) / _screen->getHeight();
+ nsRect.right = (nsRect.right * scriptsRunningWidth) / _screen->getWidth();
+ break;
+ default:
+ break;
+ }
+ writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsLeft), nsRect.left);
+ writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsTop), nsRect.top);
+ writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsRight), nsRect.right);
+ writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsBottom), nsRect.bottom);
}
- if (itemEntry->celRect.left < 0 || itemEntry->celRect.left >= _screen->getWidth()) {
- listIterator++;
- continue;
+ int16 screenHeight = _screen->getHeight();
+ int16 screenWidth = _screen->getWidth();
+ if (view->isSci2Hires()) {
+ screenHeight = _screen->getDisplayHeight();
+ screenWidth = _screen->getDisplayWidth();
}
- Common::Rect clipRect;
+ if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= screenHeight)
+ continue;
+
+ if (itemEntry->celRect.right < 0 || itemEntry->celRect.left >= screenWidth)
+ continue;
+
+ Common::Rect clipRect, translatedClipRect;
clipRect = itemEntry->celRect;
- clipRect.clip(planeRect);
+ if (view->isSci2Hires()) {
+ clipRect.clip(upscaledPlaneClipRect);
+ translatedClipRect = clipRect;
+ translatedClipRect.translate(upscaledPlaneRect.left, upscaledPlaneRect.top);
+ } else {
+ clipRect.clip(planeClipRect);
+ translatedClipRect = clipRect;
+ translatedClipRect.translate(planeRect.left, planeRect.top);
+ }
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->draw(itemEntry->celRect, clipRect, clipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, false);
- else
- view->drawScaled(itemEntry->celRect, clipRect, clipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY);
+ if (!clipRect.isEmpty()) {
+ if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
+ view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires());
+ else
+ view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY);
+ }
} else {
// Most likely a text entry
// This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap
// TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap)
- // This doesn't work for SCI2.1 games...
- if (getSciVersion() == SCI_VERSION_2) {
- Kernel *kernel = g_sci->getKernel();
- if (lookup_selector(_segMan, itemEntry->object, kernel->_selectorCache.text, NULL, NULL) == kSelectorVariable) {
- Common::String text = _segMan->getString(GET_SEL32(_segMan, itemEntry->object, SELECTOR(text)));
- int16 fontRes = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(font));
- GfxFont *font = new GfxFontFromResource(_resMan, _screen, fontRes);
- bool dimmed = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(dimmed));
- uint16 foreColor = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(fore));
- uint16 curX = itemEntry->x;
- uint16 curY = itemEntry->y;
- for (uint32 i = 0; i < text.size(); i++) {
- // TODO: proper text splitting... this is a hack
- if ((text[i] == ' ' && i > 0 && text[i - i] == ' ') || text[i] == '\n' ||
- (curX + font->getCharWidth(text[i]) > _screen->getWidth())) {
- curY += font->getHeight();
- curX = itemEntry->x;
- }
- font->draw(text[i], curY, curX, foreColor, dimmed);
- curX += font->getCharWidth(text[i]);
+ if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) {
+ reg_t stringObject = readSelector(_segMan, itemEntry->object, SELECTOR(text));
+
+ // The object in the text selector of the item can be either a raw string
+ // or a Str object. In the latter case, we need to access the object's data
+ // selector to get the raw string.
+ if (_segMan->isHeapObject(stringObject))
+ stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
+
+ Common::String text = _segMan->getString(stringObject);
+ GfxFont *font = _cache->getFont(readSelectorValue(_segMan, itemEntry->object, SELECTOR(font)));
+ bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed));
+ uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore));
+
+ itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight);
+ itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth);
+
+ uint16 curX = itemEntry->x + planeRect.left;
+ uint16 curY = itemEntry->y + planeRect.top;
+ for (uint32 i = 0; i < text.size(); i++) {
+ unsigned char curChar = text[i];
+ // TODO: proper text splitting... this is a hack
+ if ((curChar == ' ' && i > 0 && text[i - i] == ' ') || curChar == '\n' ||
+ (curX + font->getCharWidth(curChar) > _screen->getWidth())) {
+ curY += font->getHeight();
+ curX = itemEntry->x;
}
- delete font;
+ font->draw(curChar, curY, curX, foreColor, dimmed);
+ curX += font->getCharWidth(curChar);
}
}
}
- listIterator++;
}
- if (planePicture) {
- while (planePictureCel < planePictureCels) {
- planePicture->drawSci32Vga(planePictureCel);
- planePictureCel++;
+
+ for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
+ if (pictureIt->object == planeObject) {
+ delete[] pictureIt->pictureCels;
}
- delete planePicture;
- planePicture = 0;
}
}
- free(itemData);
+
+ delete[] itemData;
_screen->copyToScreen();
+
+ g_sci->getEngineState()->_throttleTrigger = true;
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 36c02af278..f8f7e54a27 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -28,7 +28,17 @@
namespace Sci {
+struct PlaneEntry {
+ reg_t object;
+ uint16 priority;
+ uint16 lastPriority;
+ GuiResourceId pictureId;
+};
+
+typedef Common::List<PlaneEntry> PlaneList;
+
struct FrameoutEntry {
+ uint16 givenOrderNr;
reg_t object;
GuiResourceId viewId;
int16 loopNo;
@@ -40,11 +50,27 @@ struct FrameoutEntry {
int16 scaleX;
int16 scaleY;
Common::Rect celRect;
+ GfxPicture *picture;
+ int16 picStartX;
};
+
typedef Common::List<FrameoutEntry *> FrameoutList;
+struct PlanePictureEntry {
+ reg_t object;
+ int16 startX;
+ GuiResourceId pictureId;
+ GfxPicture *picture;
+ FrameoutEntry *pictureCels; // temporary
+};
+
+typedef Common::List<PlanePictureEntry> PlanePictureList;
+
class GfxCache;
+class GfxCoordAdjuster32;
class GfxPaint32;
+class GfxPalette;
+class GfxScreen;
/**
* Frameout class, kFrameout and relevant functions for SCI32 games
*/
@@ -59,8 +85,12 @@ public:
void kernelAddScreenItem(reg_t object);
void kernelDeleteScreenItem(reg_t object);
int16 kernelGetHighPlanePri();
+ void kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId);
void kernelFrameout();
+ void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX);
+ void deletePlanePictures(reg_t object);
+
private:
SegManager *_segMan;
ResourceManager *_resMan;
@@ -71,8 +101,13 @@ private:
GfxPaint32 *_paint32;
Common::Array<reg_t> _screenItems;
- Common::Array<reg_t> _planes;
- int16 _highPlanePri;
+ PlaneList _planes;
+ PlanePictureList _planePictures;
+
+ void sortPlanes();
+
+ uint16 scriptsRunningWidth;
+ uint16 scriptsRunningHeight;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/gui.cpp b/engines/sci/graphics/gui.cpp
deleted file mode 100644
index 46f7fcd689..0000000000
--- a/engines/sci/graphics/gui.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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/timer.h"
-#include "common/util.h"
-
-#include "sci/sci.h"
-#include "sci/debug.h" // for g_debug_sleeptime_factor
-#include "sci/event.h"
-#include "sci/engine/state.h"
-#include "sci/engine/selector.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/screen.h"
-#include "sci/graphics/palette.h"
-#include "sci/graphics/cursor.h"
-#include "sci/graphics/ports.h"
-#include "sci/graphics/paint16.h"
-#include "sci/graphics/cache.h"
-#include "sci/graphics/compare.h"
-#include "sci/graphics/coordadjuster.h"
-#include "sci/graphics/animate.h"
-#include "sci/graphics/controls.h"
-#include "sci/graphics/menu.h"
-#include "sci/graphics/portrait.h"
-#include "sci/graphics/text16.h"
-#include "sci/graphics/transitions.h"
-#include "sci/graphics/view.h"
-#include "sci/sound/audio.h"
-
-namespace Sci {
-
-SciGui::SciGui(EngineState *state, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor, GfxPorts *ports, AudioPlayer *audio)
- : _s(state), _screen(screen), _palette(palette), _cache(cache), _cursor(cursor), _ports(ports), _audio(audio) {
-
- // FIXME/TODO: If SciGui inits all the stuff below, then it should *own* it,
- // not SciEngine. Conversely, if we want SciEngine to own this stuff,
- // then it should init it!
- _coordAdjuster = new GfxCoordAdjuster16(_ports);
- g_sci->_gfxCoordAdjuster = _coordAdjuster;
- _cursor->init(_coordAdjuster, _s->_event);
- _compare = new GfxCompare(_s->_segMan, g_sci->getKernel(), _cache, _screen, _coordAdjuster);
- g_sci->_gfxCompare = _compare;
- _transitions = new GfxTransitions(this, _screen, _palette, g_sci->getResMan()->isVGA());
- _paint16 = new GfxPaint16(g_sci->getResMan(), _s->_segMan, g_sci->getKernel(), this, _cache, _ports, _coordAdjuster, _screen, _palette, _transitions);
- g_sci->_gfxPaint = _paint16;
- g_sci->_gfxPaint16 = _paint16;
- _animate = new GfxAnimate(_s, _cache, _ports, _paint16, _screen, _palette, _cursor, _transitions);
- g_sci->_gfxAnimate = _animate;
- _text16 = new GfxText16(g_sci->getResMan(), _cache, _ports, _paint16, _screen);
- _controls = new GfxControls(_s->_segMan, _ports, _paint16, _text16, _screen);
- g_sci->_gfxControls = _controls;
- _menu = new GfxMenu(_s->_event, _s->_segMan, this, _ports, _paint16, _text16, _screen, _cursor);
- g_sci->_gfxMenu = _menu;
-}
-
-SciGui::~SciGui() {
- delete _menu;
- delete _controls;
- delete _text16;
- delete _animate;
- delete _paint16;
- delete _transitions;
- delete _compare;
- delete _coordAdjuster;
-}
-
-void SciGui::resetEngineState(EngineState *s) {
- _s = s;
- _animate->resetEngineState(s);
-}
-
-void SciGui::init(bool usesOldGfxFunctions) {
- _ports->init(usesOldGfxFunctions, this, _paint16, _text16, _s->_gameId);
- _paint16->init(_animate, _text16);
-}
-
-void SciGui::wait(int16 ticks) {
- _s->wait(ticks);
-}
-
-void SciGui::textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
- Common::Rect rect(0, 0, *textWidth, *textHeight);
- _text16->Size(rect, text, font, maxWidth);
- *textWidth = rect.width();
- *textHeight = rect.height();
-}
-
-// Used SCI1+ for text codes
-void SciGui::textFonts(int argc, reg_t *argv) {
- _text16->CodeSetFonts(argc, argv);
-}
-
-// Used SCI1+ for text codes
-void SciGui::textColors(int argc, reg_t *argv) {
- _text16->CodeSetColors(argc, argv);
-}
-
-reg_t SciGui::portraitLoad(Common::String resourceName) {
- //Portrait *myPortrait = new Portrait(g_sci->getResMan(), _screen, _palette, resourceName);
- return NULL_REG;
-}
-
-void SciGui::portraitShow(Common::String resourceName, Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq) {
- Portrait *myPortrait = new Portrait(g_sci->getResMan(), _s->_event, this, _screen, _palette, _audio, resourceName);
- // TODO: cache portraits
- // adjust given coordinates to curPort (but dont adjust coordinates on upscaledHires_Save_Box and give us hires coordinates
- // on kDrawCel, yeah this whole stuff makes sense)
- position.x += _ports->getPort()->left; position.y += _ports->getPort()->top;
- _screen->adjustToUpscaledCoordinates(position.y, position.x);
- myPortrait->doit(position, resourceId, noun, verb, cond, seq);
- delete myPortrait;
-}
-
-void SciGui::portraitUnload(uint16 portraitId) {
-}
-
-bool SciGui::debugEGAdrawingVisualize(bool state) {
- _paint16->setEGAdrawingVisualize(state);
- return false;
-}
-
-} // End of namespace Sci
diff --git a/engines/sci/graphics/gui.h b/engines/sci/graphics/gui.h
deleted file mode 100644
index 732e195026..0000000000
--- a/engines/sci/graphics/gui.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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 SCI_GRAPHICS_GUI_H
-#define SCI_GRAPHICS_GUI_H
-
-#include "sci/graphics/helpers.h"
-
-namespace Sci {
-
-class GfxScreen;
-class GfxPalette;
-class GfxCursor;
-class GfxCache;
-class GfxCompare;
-class GfxCoordAdjuster16;
-class GfxPorts;
-class GfxPaint16;
-class GfxAnimate;
-class GfxControls;
-class GfxMenu;
-class GfxText16;
-class GfxTransitions;
-
-class SciGui {
-public:
- SciGui(EngineState *s, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor, GfxPorts *ports, AudioPlayer *audio);
- virtual ~SciGui();
-
- virtual void init(bool usesOldGfxFunctions);
-
- virtual void wait(int16 ticks);
-
- virtual void textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
- virtual void textFonts(int argc, reg_t *argv);
- virtual void textColors(int argc, reg_t *argv);
-
- virtual reg_t portraitLoad(Common::String resourceName);
- virtual void portraitShow(Common::String resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
- virtual void portraitUnload(uint16 portraitId);
-
- virtual bool debugEGAdrawingVisualize(bool state);
-
- // FIXME: Don't store EngineState
- virtual void resetEngineState(EngineState *s);
-
-protected:
- GfxCursor *_cursor;
- EngineState *_s;
- GfxScreen *_screen;
- GfxPalette *_palette;
- GfxCache *_cache;
- GfxCoordAdjuster16 *_coordAdjuster;
- GfxCompare *_compare;
- GfxPorts *_ports;
- GfxPaint16 *_paint16;
-
-private:
- AudioPlayer *_audio;
- GfxAnimate *_animate;
- GfxControls *_controls;
- GfxMenu *_menu;
- GfxText16 *_text16;
- GfxTransitions *_transitions;
-
- bool _usesOldGfxFunctions;
-};
-
-} // End of namespace Sci
-
-#endif
diff --git a/engines/sci/graphics/gui32.cpp b/engines/sci/graphics/gui32.cpp
deleted file mode 100644
index 4b72050d0b..0000000000
--- a/engines/sci/graphics/gui32.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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/timer.h"
-#include "common/util.h"
-
-#include "sci/sci.h"
-#include "sci/event.h"
-#include "sci/engine/state.h"
-#include "sci/engine/selector.h"
-#include "sci/graphics/gui32.h"
-#include "sci/graphics/screen.h"
-#include "sci/graphics/palette.h"
-#include "sci/graphics/cursor.h"
-#include "sci/graphics/cache.h"
-#include "sci/graphics/compare.h"
-#include "sci/graphics/coordadjuster.h"
-#include "sci/graphics/frameout.h"
-#include "sci/graphics/paint32.h"
-#include "sci/graphics/picture.h"
-#include "sci/graphics/robot.h"
-#include "sci/graphics/view.h"
-
-namespace Sci {
-
-SciGui32::SciGui32(SegManager *segMan, SciEvent *event, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor)
- : _screen(screen), _palette(palette), _cache(cache), _cursor(cursor) {
-
- _coordAdjuster = new GfxCoordAdjuster32(segMan);
- g_sci->_gfxCoordAdjuster = _coordAdjuster;
- _cursor->init(_coordAdjuster, event);
- _compare = new GfxCompare(segMan, g_sci->getKernel(), _cache, _screen, _coordAdjuster);
- g_sci->_gfxCompare = _compare;
- _paint32 = new GfxPaint32(g_sci->getResMan(), segMan, g_sci->getKernel(), _coordAdjuster, _cache, _screen, _palette);
- g_sci->_gfxPaint = _paint32;
- _frameout = new GfxFrameout(segMan, g_sci->getResMan(), _coordAdjuster, _cache, _screen, _palette, _paint32);
- g_sci->_gfxFrameout = _frameout;
-}
-
-SciGui32::~SciGui32() {
- delete _frameout;
- delete _paint32;
- delete _compare;
- delete _coordAdjuster;
-}
-
-void SciGui32::init() {
-}
-
-void SciGui32::textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
- *textWidth = 0;
- *textHeight = 0;
-}
-
-void SciGui32::drawRobot(GuiResourceId robotId) {
- Robot *test = new Robot(g_sci->getResMan(), _screen, robotId);
- test->draw();
- delete test;
-}
-
-} // End of namespace Sci
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index f0ffecfb59..4b4cd673b4 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -28,6 +28,7 @@
#include "common/endian.h" // for READ_LE_UINT16
#include "common/rect.h"
+#include "common/serializer.h"
#include "sci/engine/vm_types.h"
namespace Sci {
@@ -54,11 +55,12 @@ struct Port {
bool greyedOutput;
int16 penClr, backClr;
int16 penMode;
+ uint16 counterTillFree;
Port(uint16 theId) : id(theId), top(0), left(0),
curTop(0), curLeft(0),
fontHeight(0), fontId(0), greyedOutput(false),
- penClr(0), backClr(0xFF), penMode(0) {
+ penClr(0), backClr(0xFF), penMode(0), counterTillFree(0) {
}
};
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
new file mode 100644
index 0000000000..6f2c9596db
--- /dev/null
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -0,0 +1,92 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "sci/sci.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/maciconbar.h"
+#include "sci/graphics/palette.h"
+
+#include "common/stream.h"
+#include "common/system.h"
+#include "graphics/pict.h"
+#include "graphics/surface.h"
+
+namespace Sci {
+
+void GfxMacIconBar::addIcon(reg_t obj) {
+ _iconBarObjects.push_back(obj);
+}
+
+void GfxMacIconBar::drawIcons() {
+ // Draw the icons to the bottom of the screen
+
+ byte *pal = new byte[256 * 4];
+ Graphics::PictDecoder *pict = new Graphics::PictDecoder(Graphics::PixelFormat::createFormatCLUT8());
+ uint32 lastX = 0;
+
+ for (uint32 i = 0; i < _iconBarObjects.size(); i++) {
+ uint32 iconIndex = readSelectorValue(g_sci->getEngineState()->_segMan, _iconBarObjects[i], SELECTOR(iconIndex));
+ Resource *res = g_sci->getResMan()->findResource(ResourceId(kResourceTypeMacIconBarPictN, iconIndex + 1), false);
+ if (!res)
+ continue;
+
+ Common::MemoryReadStream *stream = new Common::MemoryReadStream(res->data, res->size);
+ Graphics::Surface *surf = pict->decodeImage(stream, pal);
+ remapColors(surf, pal);
+
+ g_system->copyRectToScreen((byte *)surf->pixels, surf->pitch, lastX, 200, MIN<uint32>(surf->w, 320 - lastX), surf->h);
+
+ lastX += surf->w;
+ surf->free();
+ delete surf;
+ delete stream;
+ }
+
+ delete pict;
+ delete[] pal;
+}
+
+void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) {
+ byte *pixels = (byte *)surf->pixels;
+
+ // Remap to the screen palette
+ for (uint16 i = 0; i < surf->w * surf->h; i++) {
+ byte color = *pixels;
+
+ byte r = palette[color * 4];
+ byte g = palette[color * 4 + 1];
+ byte b = palette[color * 4 + 2];
+
+ // For black, make sure the index is 0
+ if (r == 0 && g == 0 && b == 0)
+ *pixels++ = 0;
+ else
+ *pixels++ = g_sci->_gfxPalette->kernelFindColor(r, g, b);
+ }
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/gui32.h b/engines/sci/graphics/maciconbar.h
index 99eb03b321..71e65fcb40 100644
--- a/engines/sci/graphics/gui32.h
+++ b/engines/sci/graphics/maciconbar.h
@@ -23,44 +23,31 @@
*
*/
-#ifndef SCI_GRAPHICS_GUI32_H
-#define SCI_GRAPHICS_GUI32_H
+#ifndef SCI_GRAPHICS_MACICONBAR_H
+#define SCI_GRAPHICS_MACICONBAR_H
-#include "sci/graphics/helpers.h"
+#include "common/array.h"
-namespace Sci {
-
-class GfxCursor;
-class GfxScreen;
-class GfxPalette;
-class GfxCache;
-class GfxCoordAdjuster32;
-class GfxCompare;
-class GfxFrameout;
-class GfxPaint32;
-
-class SciGui32 {
-public:
- SciGui32(SegManager *segMan, SciEvent *event, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor);
- ~SciGui32();
+#include "sci/engine/vm.h"
- void init();
+namespace Graphics {
+ struct Surface;
+}
- void textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
+namespace Sci {
- void drawRobot(GuiResourceId robotId);
+class GfxMacIconBar {
+public:
+ GfxMacIconBar() {}
+ ~GfxMacIconBar() {}
-protected:
- GfxCursor *_cursor;
- GfxScreen *_screen;
- GfxPalette *_palette;
- GfxCache *_cache;
- GfxCoordAdjuster32 *_coordAdjuster;
- GfxCompare *_compare;
- GfxFrameout *_frameout;
- GfxPaint32 *_paint32;
+ void addIcon(reg_t obj);
+ void drawIcons();
private:
+ Common::Array<reg_t> _iconBarObjects;
+
+ void remapColors(Graphics::Surface *surf, byte *palette);
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 5e3b419fe3..630626c128 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -29,9 +29,9 @@
#include "sci/sci.h"
#include "sci/event.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/paint16.h"
#include "sci/graphics/animate.h"
@@ -43,8 +43,8 @@
namespace Sci {
-GfxMenu::GfxMenu(SciEvent *event, SegManager *segMan, SciGui *gui, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor)
- : _event(event), _segMan(segMan), _gui(gui), _ports(ports), _paint16(paint16), _text16(text16), _screen(screen), _cursor(cursor) {
+GfxMenu::GfxMenu(EventManager *event, SegManager *segMan, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor)
+ : _event(event), _segMan(segMan), _ports(ports), _paint16(paint16), _text16(text16), _screen(screen), _cursor(cursor) {
_menuSaveHandle = NULL_REG;
_barSaveHandle = NULL_REG;
@@ -63,8 +63,9 @@ void GfxMenu::reset() {
_itemList.clear();
_listCount = 0;
- // We actually set active item in here and remember last selection of the user
- // sierra sci always defaulted to first item every time menu was called via ESC, we dont follow that logic
+ // We actually set active item in here and remember last selection of the
+ // user. Sierra SCI always defaulted to first item every time menu was
+ // called via ESC, we don't follow that logic.
_curMenuId = 1;
_curItemId = 1;
}
@@ -92,15 +93,16 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
beginPos = curPos;
- // Now go through the content till we find end-marker and collect data about it
- // ':' is an end-marker for each item
+ // Now go through the content till we find end-marker and collect data about it.
+ // ':' is an end-marker for each item.
tagPos = 0; rightAlignedPos = 0;
controlPos = 0; altPos = 0; functionPos = 0;
while ((curPos < contentSize) && (content[curPos] != ':')) {
switch (content[curPos]) {
case '=': // Set tag
- // Special case for normal animation speed - they use right aligned "=" for that one, so we ignore it
- // as being recognized as tag marker
+ // Special case for normal animation speed - they use right
+ // aligned "=" for that one, so we ignore it as being recognized
+ // as tag marker.
if (rightAlignedPos == curPos - 1)
break;
if (tagPos)
@@ -199,8 +201,9 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
if (separatorCount == tempPos - beginPos) {
itemEntry->separatorLine = true;
} else {
- // we don't strSplit here, because multilingual SCI01 support language switching on the fly, so we have to do
- // this everytime the menu is called
+ // We don't strSplit here, because multilingual SCI01 support
+ // language switching on the fly, so we have to do this everytime
+ // the menu is called.
itemEntry->text = Common::String(content.c_str() + beginPos, tempPos - beginPos);
// LSL6 uses "Ctrl-" prefix string instead of ^ like all the other games do
@@ -222,10 +225,12 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
if (tagPos && tagPos >= rightAlignedPos)
tempPos = tagPos;
itemEntry->textRightAligned = Common::String(content.c_str() + rightAlignedPos, tempPos - rightAlignedPos);
- // Remove ending space, if there is one. Strangely sometimes there are lone spaces at the end in some games
+ // Remove ending space, if there is one. Strangely sometimes there
+ // are lone spaces at the end in some games
if (itemEntry->textRightAligned.hasSuffix(" "))
itemEntry->textRightAligned.deleteLastChar();
- // - and + are used sometimes for volume control/animation speed, = sometimes for animation speed
+ // - and + are used sometimes for volume control/animation speed,
+ // = sometimes for animation speed
if (itemEntry->textRightAligned == "-") {
itemEntry->keyPress = '-';
} else if (itemEntry->textRightAligned == "+") {
@@ -266,8 +271,15 @@ GuiMenuItemEntry *GfxMenu::findItem(uint16 menuId, uint16 itemId) {
void GfxMenu::kernelSetAttribute(uint16 menuId, uint16 itemId, uint16 attributeId, reg_t value) {
GuiMenuItemEntry *itemEntry = findItem(menuId, itemId);
- if (!itemEntry)
- error("Tried to setAttribute() on non-existant menu-item %d:%d", menuId, itemId);
+
+ if (!itemEntry) {
+ // PQ2 demo calls this, for example, but has no menus (bug report #3034507). Some SCI
+ // fan games (Al Pond 2, Aquarius) call this too on non-existent menu items. The
+ // original interpreter ignored these as well.
+ debugC(2, kDebugLevelGraphics, "Tried to setAttribute() on non-existent menu-item %d:%d", menuId, itemId);
+ return;
+ }
+
switch (attributeId) {
case SCI_MENU_ATTRIBUTE_ENABLED:
itemEntry->enabled = value.isNull() ? false : true;
@@ -377,8 +389,8 @@ void GfxMenu::calculateMenuAndItemWidth() {
}
}
-reg_t GfxMenu::kernelSelect(reg_t eventObject) {
- int16 eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type));
+reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) {
+ int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
int16 keyPress, keyModifier;
Common::Point mousePosition;
GuiMenuItemList::iterator itemIterator = _itemList.begin();
@@ -386,13 +398,13 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
GuiMenuItemEntry *itemEntry = NULL;
bool forceClaimed = false;
EngineState *s;
- byte saidSpec[64];
switch (eventType) {
case SCI_EVENT_KEYBOARD:
- keyPress = GET_SEL32V(_segMan, eventObject, SELECTOR(message));
- keyModifier = GET_SEL32V(_segMan, eventObject, SELECTOR(modifiers));
- // If tab got pressed, handle it here as if it was Ctrl-I - at least sci0 also did it that way
+ keyPress = readSelectorValue(_segMan, eventObject, SELECTOR(message));
+ keyModifier = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers));
+ // If tab got pressed, handle it here as if it was Ctrl-I - at least
+ // sci0 also did it that way
if (keyPress == SCI_KEY_TAB) {
keyModifier = SCI_KEYMOD_CTRL;
keyPress = 'i';
@@ -401,9 +413,9 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
case 0:
break;
case SCI_KEY_ESC:
- interactiveShowMouse();
+ interactiveStart(pauseSound);
itemEntry = interactiveWithKeyboard();
- interactiveRestoreMouse();
+ interactiveEnd(pauseSound);
forceClaimed = true;
break;
default:
@@ -425,8 +437,13 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
itemEntry = *itemIterator;
if (!itemEntry->saidVmPtr.isNull()) {
- // TODO: get a pointer to saidVmPtr or make said() work on VmPtrs
- _segMan->memcpy(saidSpec, itemEntry->saidVmPtr, 64);
+ byte *saidSpec = _segMan->derefBulkPtr(itemEntry->saidVmPtr, 0);
+
+ if (!saidSpec) {
+ warning("Could not dereference saidSpec");
+ continue;
+ }
+
if (said(s, saidSpec, 0) != SAID_NO_MATCH)
break;
}
@@ -439,9 +456,9 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
case SCI_EVENT_MOUSE_PRESS:
mousePosition = _cursor->getPosition();
if (mousePosition.y < 10) {
- interactiveShowMouse();
+ interactiveStart(pauseSound);
itemEntry = interactiveWithMouse();
- interactiveRestoreMouse();
+ interactiveEnd(pauseSound);
forceClaimed = true;
}
break;
@@ -461,11 +478,13 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
_paint16->bitsShow(_ports->_menuRect);
_barSaveHandle = NULL_REG;
}
- if (_oldPort)
+ if (_oldPort) {
_ports->setPort(_oldPort);
+ _oldPort = NULL;
+ }
if ((itemEntry) || (forceClaimed))
- PUT_SEL32(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1));
+ writeSelector(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1));
if (itemEntry)
return make_reg(0, (itemEntry->menuId << 8) | (itemEntry->id));
return NULL_REG;
@@ -562,7 +581,8 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
if (!maxTextRightAlignedWidth)
_menuRect.right -= 5;
- // if part of menu window is outside the screen, move it into the screen (this happens in multilingual sq3 and lsl3)
+ // If part of menu window is outside the screen, move it into the screen
+ // (this happens in multilingual sq3 and lsl3).
if (_menuRect.right > _screen->getWidth()) {
_menuRect.translate(-(_menuRect.right - _screen->getWidth()), 0);
}
@@ -625,12 +645,16 @@ void GfxMenu::invertMenuSelection(uint16 itemId) {
_paint16->bitsShow(itemRect);
}
-void GfxMenu::interactiveShowMouse() {
+void GfxMenu::interactiveStart(bool pauseSound) {
_mouseOldState = _cursor->isVisible();
_cursor->kernelShow();
+ if (pauseSound)
+ g_sci->_soundCmd->pauseAll(true);
}
-void GfxMenu::interactiveRestoreMouse() {
+void GfxMenu::interactiveEnd(bool pauseSound) {
+ if (pauseSound)
+ g_sci->_soundCmd->pauseAll(false);
if (!_mouseOldState)
_cursor->kernelHide();
}
@@ -682,18 +706,19 @@ uint16 GfxMenu::mouseFindMenuItemSelection(Common::Point mousePosition, uint16 m
}
GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
- sciEvent curEvent;
+ SciEvent curEvent;
uint16 newMenuId = _curMenuId;
uint16 newItemId = _curItemId;
GuiMenuItemEntry *curItemEntry = findItem(_curMenuId, _curItemId);
GuiMenuItemEntry *newItemEntry = curItemEntry;
Common::Point mousePosition;
- // We don't 100% follow sierra here: we select last item instead of selecting first item of first menu everytime
- // Also sierra sci didnt allow mouse interaction, when menu was activated via keyboard
+ // We don't 100% follow Sierra here: we select last item instead of
+ // selecting first item of first menu every time. Also sierra sci didn't
+ // allow mouse interaction, when menu was activated via keyboard.
- calculateMenuAndItemWidth();
_oldPort = _ports->setPort(_ports->_menuPort);
+ calculateMenuAndItemWidth();
_barSaveHandle = _paint16->bitsSave(_ports->_menuRect, GFX_SCREEN_MASK_VISUAL);
_ports->penColor(0);
@@ -706,12 +731,13 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
_paint16->bitsShow(_menuRect);
while (true) {
- curEvent = _event->get(SCI_EVENT_ANY);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
switch (curEvent.type) {
case SCI_EVENT_KEYBOARD:
- // We don't 100% follow sierra here: - sierra didn't wrap around when changing item id
- // - sierra allowed item id to be 0, which didnt make any sense
+ // We don't 100% follow sierra here:
+ // - sierra didn't wrap around when changing item id
+ // - sierra allowed item id to be 0, which didn't make any sense
do {
switch (curEvent.data) {
case SCI_KEY_ESC:
@@ -796,25 +822,26 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
}
break;
case SCI_EVENT_NONE:
- _event->sleep(2500 / 1000);
+ g_sci->sleep(2500 / 1000);
break;
}
}
}
-// Mouse button is currently pressed - we are now interpreting mouse coordinates till mouse button is released
-// The menu item that is selected at that time is chosen. If no menu item is selected we cancel
-// No keyboard interaction is allowed, cause that wouldnt make any sense at all
+// Mouse button is currently pressed - we are now interpreting mouse coordinates
+// till mouse button is released. The menu item that is selected at that time is
+// chosen. If no menu item is selected we cancel. No keyboard interaction is
+// allowed, cause that wouldnt make any sense at all.
GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
- sciEvent curEvent;
+ SciEvent curEvent;
uint16 newMenuId = 0, newItemId = 0;
uint16 curMenuId = 0, curItemId = 0;
Common::Point mousePosition = _cursor->getPosition();
bool firstMenuChange = true;
GuiMenuItemEntry *curItemEntry = NULL;
- calculateMenuAndItemWidth();
_oldPort = _ports->setPort(_ports->_menuPort);
+ calculateMenuAndItemWidth();
_barSaveHandle = _paint16->bitsSave(_ports->_menuRect, GFX_SCREEN_MASK_VISUAL);
_ports->penColor(0);
@@ -824,7 +851,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
_paint16->bitsShow(_ports->_menuRect);
while (true) {
- curEvent = _event->get(SCI_EVENT_ANY);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
switch (curEvent.type) {
case SCI_EVENT_MOUSE_RELEASE:
@@ -835,7 +862,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
return curItemEntry;
case SCI_EVENT_NONE:
- _event->sleep(2500 / 1000);
+ g_sci->sleep(2500 / 1000);
break;
}
diff --git a/engines/sci/graphics/menu.h b/engines/sci/graphics/menu.h
index 8f23b46ff8..9a14d4c64a 100644
--- a/engines/sci/graphics/menu.h
+++ b/engines/sci/graphics/menu.h
@@ -83,7 +83,7 @@ typedef Common::List<GuiMenuItemEntry *> GuiMenuItemList;
*/
class GfxMenu {
public:
- GfxMenu(SciEvent *event, SegManager *segMan, SciGui *gui, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor);
+ GfxMenu(EventManager *event, SegManager *segMan, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor);
~GfxMenu();
void reset();
@@ -92,7 +92,7 @@ public:
reg_t kernelGetAttribute(uint16 menuId, uint16 itemId, uint16 attributeId);
void drawBar();
- reg_t kernelSelect(reg_t eventObject);
+ reg_t kernelSelect(reg_t eventObject, bool pauseSound);
void kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack);
void kernelDrawMenuBar(bool clear);
@@ -103,17 +103,16 @@ private:
void calculateMenuAndItemWidth();
void drawMenu(uint16 oldMenuId, uint16 newMenuId);
void invertMenuSelection(uint16 itemId);
- void interactiveShowMouse();
- void interactiveRestoreMouse();
+ void interactiveStart(bool pauseSound);
+ void interactiveEnd(bool pauseSound);
GuiMenuItemEntry *interactiveWithKeyboard();
GuiMenuItemEntry *interactiveWithMouse();
uint16 mouseFindMenuSelection(Common::Point mousePosition);
uint16 mouseFindMenuItemSelection(Common::Point mousePosition, uint16 menuId);
GuiMenuItemEntry *interactiveGetItem(uint16 menuId, uint16 itemId, bool menuChanged);
- SciEvent *_event;
+ EventManager *_event;
SegManager *_segMan;
- SciGui *_gui;
GfxPorts *_ports;
GfxPaint16 *_paint16;
GfxText16 *_text16;
diff --git a/engines/sci/graphics/paint.cpp b/engines/sci/graphics/paint.cpp
index 13dcebc27a..50b0534ba7 100644
--- a/engines/sci/graphics/paint.cpp
+++ b/engines/sci/graphics/paint.cpp
@@ -49,8 +49,4 @@ void GfxPaint::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, ui
void GfxPaint::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) {
}
-void GfxPaint::kernelShakeScreen(uint16 shakeCount, uint16 directions) {
- warning("Unimplemented kShakeScreen");
-}
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/paint.h b/engines/sci/graphics/paint.h
index f1342d55e5..994bc4e5e9 100644
--- a/engines/sci/graphics/paint.h
+++ b/engines/sci/graphics/paint.h
@@ -26,8 +26,6 @@
#ifndef SCI_GRAPHICS_PAINT_H
#define SCI_GRAPHICS_PAINT_H
-#include "sci/graphics/gui.h"
-
#include "common/hashmap.h"
namespace Sci {
@@ -40,10 +38,6 @@ public:
virtual void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
virtual void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
virtual void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
-
- virtual void kernelShakeScreen(uint16 shakeCount, uint16 directions);
-
-private:
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index d0975f3d3d..4551e9dafc 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -32,6 +32,7 @@
#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
+#include "sci/engine/workarounds.h"
#include "sci/graphics/cache.h"
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/ports.h"
@@ -42,13 +43,14 @@
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/portrait.h"
#include "sci/graphics/text16.h"
#include "sci/graphics/transitions.h"
namespace Sci {
-GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, SciGui *gui, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions)
- : _resMan(resMan), _segMan(segMan), _kernel(kernel), _gui(gui), _cache(cache), _ports(ports), _coordAdjuster(coordAdjuster), _screen(screen), _palette(palette), _transitions(transitions) {
+GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio)
+ : _resMan(resMan), _segMan(segMan), _kernel(kernel), _cache(cache), _ports(ports), _coordAdjuster(coordAdjuster), _screen(screen), _palette(palette), _transitions(transitions), _audio(audio) {
}
GfxPaint16::~GfxPaint16() {
@@ -61,7 +63,7 @@ void GfxPaint16::init(GfxAnimate *animate, GfxText16 *text16) {
_EGAdrawingVisualize = false;
}
-void GfxPaint16::setEGAdrawingVisualize(bool state) {
+void GfxPaint16::debugSetEGAdrawingVisualize(bool state) {
_EGAdrawingVisualize = state;
}
@@ -74,6 +76,11 @@ void GfxPaint16::drawPicture(GuiResourceId pictureId, int16 animationNr, bool mi
picture->draw(animationNr, mirroredFlag, addToFlag, paletteId);
delete picture;
+
+ // We make a call to SciPalette here, for increasing sys timestamp and also loading targetpalette, if palvary active
+ // (SCI1.1 only)
+ if (getSciVersion() == SCI_VERSION_1_1)
+ _palette->drewPicture(pictureId);
}
// This one is the only one that updates screen!
@@ -101,12 +108,12 @@ void GfxPaint16::drawCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo,
}
// This version of drawCel is not supposed to call BitsShow()!
-void GfxPaint16::drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
+void GfxPaint16::drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
drawCel(_cache->getView(viewId), loopNo, celNo, celRect, priority, paletteNo, scaleX, scaleY);
}
// This version of drawCel is not supposed to call BitsShow()!
-void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
+void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
Common::Rect clipRect = celRect;
clipRect.clip(_ports->_curPort->rect);
if (clipRect.isEmpty()) // nothing to draw
@@ -121,8 +128,8 @@ void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, Common::Rect
}
}
-// This is used as replacement for drawCelAndShow() when hires-cels are drawn to screen
-// Hires-cels are available only SCI 1.1+
+// This is used as replacement for drawCelAndShow() when hires-cels are drawn to
+// screen. Hires-cels are available only SCI 1.1+.
void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, reg_t upscaledHiresHandle, uint16 scaleX, uint16 scaleY) {
GfxView *view = _cache->getView(viewId);
Common::Rect celRect, curPortRect, clipRect, clipRectTranslated;
@@ -131,9 +138,10 @@ void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 c
if (view) {
if ((leftPos == 0) && (topPos == 0)) {
- // HACK: in kq6, we get leftPos&topPos == 0 SOMETIMES, that's why we need to get coordinates from upscaledHiresHandle
- // I'm not sure if this is what we are supposed to do or if there is some other bug that actually makes
- // coordinates to be 0 in the first place
+ // HACK: in kq6, we get leftPos&topPos == 0 SOMETIMES, that's why we
+ // need to get coordinates from upscaledHiresHandle. I'm not sure if
+ // this is what we are supposed to do or if there is some other bug
+ // that actually makes coordinates to be 0 in the first place.
byte *memoryPtr = NULL;
memoryPtr = _segMan->getHunkPointer(upscaledHiresHandle);
if (memoryPtr) {
@@ -310,11 +318,12 @@ reg_t GfxPaint16::bitsSave(const Common::Rect &rect, byte screenMask) {
return NULL_REG;
if (screenMask == GFX_SCREEN_MASK_DISPLAY) {
+ // The coordinates we are given are actually up-to-including right/bottom - we extend accordingly
+ workerRect.bottom++;
+ workerRect.right++;
// Adjust rect to upscaled hires, but dont adjust according to port
_screen->adjustToUpscaledCoordinates(workerRect.top, workerRect.left);
_screen->adjustToUpscaledCoordinates(workerRect.bottom, workerRect.right);
- workerRect.bottom++;
- workerRect.right++;
} else {
_ports->offsetRect(workerRect);
}
@@ -324,7 +333,8 @@ reg_t GfxPaint16::bitsSave(const Common::Rect &rect, byte screenMask) {
memoryId = _segMan->allocateHunkEntry("SaveBits()", size);
memoryPtr = _segMan->getHunkPointer(memoryId);
- _screen->bitsSave(workerRect, screenMask, memoryPtr);
+ if (memoryPtr)
+ _screen->bitsSave(workerRect, screenMask, memoryPtr);
return memoryId;
}
@@ -354,7 +364,8 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) {
}
void GfxPaint16::bitsFree(reg_t memoryHandle) {
- _segMan->freeHunkEntry(memoryHandle);
+ if (!memoryHandle.isNull()) // happens in KQ5CD
+ _segMan->freeHunkEntry(memoryHandle);
}
void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) {
@@ -372,28 +383,29 @@ void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b
_ports->setPort(oldPort);
}
-void GfxPaint16::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) {
- // some calls are hiresMode even under kq6 DOS, that's why we check for upscaled hires here
+void GfxPaint16::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY, bool hiresMode, reg_t upscaledHiresHandle) {
+ // some calls are hiresMode even under kq6 DOS, that's why we check for
+ // upscaled hires here
if ((!hiresMode) || (!_screen->getUpscaledHires())) {
- drawCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo);
+ drawCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo, scaleX, scaleY);
} else {
drawHiresCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo, upscaledHiresHandle);
}
}
-void GfxPaint16::kernelGraphFillBoxForeground(Common::Rect rect) {
+void GfxPaint16::kernelGraphFillBoxForeground(const Common::Rect &rect) {
paintRect(rect);
}
-void GfxPaint16::kernelGraphFillBoxBackground(Common::Rect rect) {
+void GfxPaint16::kernelGraphFillBoxBackground(const Common::Rect &rect) {
eraseRect(rect);
}
-void GfxPaint16::kernelGraphFillBox(Common::Rect rect, uint16 colorMask, int16 color, int16 priority, int16 control) {
+void GfxPaint16::kernelGraphFillBox(const Common::Rect &rect, uint16 colorMask, int16 color, int16 priority, int16 control) {
fillRect(rect, colorMask, color, priority, control);
}
-void GfxPaint16::kernelGraphFrameBox(Common::Rect rect, int16 color) {
+void GfxPaint16::kernelGraphFrameBox(const Common::Rect &rect, int16 color) {
int16 oldColor = _ports->getPort()->penClr;
_ports->penColor(color);
frameRect(rect);
@@ -405,11 +417,11 @@ void GfxPaint16::kernelGraphDrawLine(Common::Point startPoint, Common::Point end
_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}
-reg_t GfxPaint16::kernelGraphSaveBox(Common::Rect rect, uint16 screenMask) {
+reg_t GfxPaint16::kernelGraphSaveBox(const Common::Rect &rect, uint16 screenMask) {
return bitsSave(rect, screenMask);
}
-reg_t GfxPaint16::kernelGraphSaveUpscaledHiresBox(Common::Rect rect) {
+reg_t GfxPaint16::kernelGraphSaveUpscaledHiresBox(const Common::Rect &rect) {
return bitsSave(rect, GFX_SCREEN_MASK_DISPLAY);
}
@@ -417,8 +429,9 @@ void GfxPaint16::kernelGraphRestoreBox(reg_t handle) {
bitsRestore(handle);
}
-void GfxPaint16::kernelGraphUpdateBox(Common::Rect rect, bool hiresMode) {
- // some calls are hiresMode even under kq6 DOS, that's why we check for upscaled hires here
+void GfxPaint16::kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode) {
+ // some calls are hiresMode even under kq6 DOS, that's why we check for
+ // upscaled hires here
if ((!hiresMode) || (!_screen->getUpscaledHires()))
bitsShow(rect);
else
@@ -446,17 +459,20 @@ void GfxPaint16::kernelGraphRedrawBox(Common::Rect rect) {
#define SCI_DISPLAY_WIDTH 106
#define SCI_DISPLAY_SAVEUNDER 107
#define SCI_DISPLAY_RESTOREUNDER 108
+#define SCI_DISPLAY_DUMMY1 114 // used in longbow demo/qfg1 ega demo, not supported in sierra sci - no parameters
+#define SCI_DISPLAY_DUMMY2 115 // used in longbow demo, not supported in sierra sci - has 1 parameter
#define SCI_DISPLAY_DONTSHOWBITS 121
reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
- int displayArg;
+ reg_t displayArg;
TextAlignment alignment = SCI_TEXT16_ALIGNMENT_LEFT;
int16 colorPen = -1, colorBack = -1, width = -1, bRedraw = 1;
bool doSaveUnder = false;
Common::Rect rect;
reg_t result = NULL_REG;
- // Make a "backup" of the port settings (required for some SCI0LATE and SCI01+ only)
+ // Make a "backup" of the port settings (required for some SCI0LATE and
+ // SCI01+ only)
Port oldPort = *_ports->getPort();
// setting defaults
@@ -465,9 +481,11 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
_ports->textGreyedOutput(false);
// processing codes in argv
while (argc > 0) {
- displayArg = argv[0].toUint16();
+ displayArg = argv[0];
+ if (displayArg.segment)
+ displayArg.offset = 0xFFFF;
argc--; argv++;
- switch (displayArg) {
+ switch (displayArg.offset) {
case SCI_DISPLAY_MOVEPEN:
_ports->moveTo(argv[0].toUint16(), argv[1].toUint16());
argc -= 2; argv += 2;
@@ -511,8 +529,27 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
case SCI_DISPLAY_DONTSHOWBITS:
bRedraw = 0;
break;
+
+ // 2 Dummy functions, longbow-demo is using those several times but sierra sci doesn't support them at all
+ // The Quest for Glory 1 EGA demo also calls kDisplay(114)
+ case SCI_DISPLAY_DUMMY1:
+ case SCI_DISPLAY_DUMMY2:
+ if (!g_sci->isDemo() || (g_sci->getGameId() != GID_LONGBOW && g_sci->getGameId() != GID_QFG1))
+ error("Unknown kDisplay argument %d", displayArg.offset);
+ if (displayArg.offset == SCI_DISPLAY_DUMMY2) {
+ if (argc) {
+ argc--; argv++;
+ } else {
+ error("No parameter left for kDisplay(115)");
+ }
+ }
+ break;
default:
- warning("Unknown kDisplay argument %X", displayArg);
+ SciTrackOriginReply originReply;
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply);
+ if (solution.type == WORKAROUND_NONE)
+ error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)", PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
+ assert(solution.type == WORKAROUND_IGNORE);
break;
}
}
@@ -539,14 +576,20 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
uint16 tTop = currport->curTop;
uint16 tLeft = currport->curLeft;
if (!g_sci->_features->usesOldGfxFunctions()) {
- // Restore port settings for some SCI0LATE and SCI01+ only
- // the change actually happened inbetween .530 (hoyle1) and .566 (heros quest). We don't have any detection for
- // that currently, so we are using oldGfxFunctions (.502). The only games that could get regressions because of
- // this are hoyle1, kq4 and funseeker. If there are regressions, we should use interpreter version (which would
- // require exe version detection)
- // If we restore the port for whole SCI0LATE, at least sq3old will get an issue - font 0 will get used when
- // scanning for planets instead of font 600 - a setfont parameter is missing in one of the kDisplay calls in
- // script 19. I assume this is a script bug, because it was added in sq3new.
+ // Restore port settings for some SCI0LATE and SCI01+ only.
+ //
+ // The change actually happened inbetween .530 (hoyle1) and .566 (heros
+ // quest). We don't have any detection for that currently, so we are
+ // using oldGfxFunctions (.502). The only games that could get
+ // regressions because of this are hoyle1, kq4 and funseeker. If there
+ // are regressions, we should use interpreter version (which would
+ // require exe version detection).
+ //
+ // If we restore the port for whole SCI0LATE, at least sq3old will get
+ // an issue - font 0 will get used when scanning for planets instead of
+ // font 600 - a setfont parameter is missing in one of the kDisplay
+ // calls in script 19. I assume this is a script bug, because it was
+ // added in sq3new.
*currport = oldPort;
}
currport->curTop = tTop;
@@ -554,19 +597,23 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
return result;
}
-// TODO: If this matches the sci32 implementation, we may put it into GfxScreen
-void GfxPaint16::kernelShakeScreen(uint16 shakeCount, uint16 directions) {
- while (shakeCount--) {
- if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
- _screen->setVerticalShakePos(10);
- // TODO: horizontal shakes
- g_system->updateScreen();
- _gui->wait(3);
- if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
- _screen->setVerticalShakePos(0);
- g_system->updateScreen();
- _gui->wait(3);
- }
+reg_t GfxPaint16::kernelPortraitLoad(const Common::String &resourceName) {
+ //Portrait *myPortrait = new Portrait(g_sci->getResMan(), _screen, _palette, resourceName);
+ return NULL_REG;
+}
+
+void GfxPaint16::kernelPortraitShow(const Common::String &resourceName, Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq) {
+ Portrait *myPortrait = new Portrait(g_sci->getResMan(), g_sci->getEventManager(), _screen, _palette, _audio, resourceName);
+ // TODO: cache portraits
+ // adjust given coordinates to curPort (but dont adjust coordinates on upscaledHires_Save_Box and give us hires coordinates
+ // on kDrawCel, yeah this whole stuff makes sense)
+ position.x += _ports->getPort()->left; position.y += _ports->getPort()->top;
+ _screen->adjustToUpscaledCoordinates(position.y, position.x);
+ myPortrait->doit(position, resourceId, noun, verb, cond, seq);
+ delete myPortrait;
+}
+
+void GfxPaint16::kernelPortraitUnload(uint16 portraitId) {
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h
index b18c879387..e944c71bdd 100644
--- a/engines/sci/graphics/paint16.h
+++ b/engines/sci/graphics/paint16.h
@@ -26,7 +26,6 @@
#ifndef SCI_GRAPHICS_PAINT16_H
#define SCI_GRAPHICS_PAINT16_H
-#include "sci/graphics/gui.h"
#include "sci/graphics/paint.h"
#include "common/hashmap.h"
@@ -45,17 +44,17 @@ class GfxView;
*/
class GfxPaint16 : public GfxPaint {
public:
- GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, SciGui *gui, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions);
+ GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio);
~GfxPaint16();
void init(GfxAnimate *animate, GfxText16 *text16);
- void setEGAdrawingVisualize(bool state);
+ void debugSetEGAdrawingVisualize(bool state);
void drawPicture(GuiResourceId pictureId, int16 animationNr, bool mirroredFlag, bool addToFlag, GuiResourceId paletteId);
void drawCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
- void drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
- void drawCel(GfxView *view, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
+ void drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
+ void drawCel(GfxView *view, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
void drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, reg_t upscaledHiresHandle, uint16 scaleX = 128, uint16 scaleY = 128);
void clearScreen(byte color = 255);
@@ -74,28 +73,30 @@ public:
void bitsFree(reg_t memoryHandle);
void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
- void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
+ void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY, bool hiresMode, reg_t upscaledHiresHandle);
- void kernelGraphFillBoxForeground(Common::Rect rect);
- void kernelGraphFillBoxBackground(Common::Rect rect);
- void kernelGraphFillBox(Common::Rect rect, uint16 colorMask, int16 color, int16 priority, int16 control);
- void kernelGraphFrameBox(Common::Rect rect, int16 color);
+ void kernelGraphFillBoxForeground(const Common::Rect &rect);
+ void kernelGraphFillBoxBackground(const Common::Rect &rect);
+ void kernelGraphFillBox(const Common::Rect &rect, uint16 colorMask, int16 color, int16 priority, int16 control);
+ void kernelGraphFrameBox(const Common::Rect &rect, int16 color);
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
- reg_t kernelGraphSaveBox(Common::Rect rect, uint16 flags);
- reg_t kernelGraphSaveUpscaledHiresBox(Common::Rect rect);
+ reg_t kernelGraphSaveBox(const Common::Rect &rect, uint16 flags);
+ reg_t kernelGraphSaveUpscaledHiresBox(const Common::Rect &rect);
void kernelGraphRestoreBox(reg_t handle);
- void kernelGraphUpdateBox(Common::Rect rect, bool hiresMode);
+ void kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode);
void kernelGraphRedrawBox(Common::Rect rect);
reg_t kernelDisplay(const char *text, int argc, reg_t *argv);
- void kernelShakeScreen(uint16 shakeCount, uint16 directions);
+ reg_t kernelPortraitLoad(const Common::String &resourceName);
+ void kernelPortraitShow(const Common::String &resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
+ void kernelPortraitUnload(uint16 portraitId);
private:
ResourceManager *_resMan;
SegManager *_segMan;
Kernel *_kernel;
- SciGui *_gui;
+ AudioPlayer *_audio;
GfxAnimate *_animate;
GfxCache *_cache;
GfxPorts *_ports;
diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp
index 711efc9816..9b24da413b 100644
--- a/engines/sci/graphics/paint32.cpp
+++ b/engines/sci/graphics/paint32.cpp
@@ -38,6 +38,7 @@
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/robot.h"
namespace Sci {
@@ -79,4 +80,10 @@ void GfxPaint32::kernelGraphDrawLine(Common::Point startPoint, Common::Point end
_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}
+void GfxPaint32::debugDrawRobot(GuiResourceId robotId) {
+ GfxRobot *test = new GfxRobot(g_sci->getResMan(), _screen, robotId);
+ test->draw();
+ delete test;
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h
index f4d6340361..a048d7f307 100644
--- a/engines/sci/graphics/paint32.h
+++ b/engines/sci/graphics/paint32.h
@@ -26,7 +26,6 @@
#ifndef SCI_GRAPHICS_PAINT32_H
#define SCI_GRAPHICS_PAINT32_H
-#include "sci/graphics/gui.h"
#include "sci/graphics/paint.h"
#include "common/hashmap.h"
@@ -49,6 +48,8 @@ public:
void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
+ void debugDrawRobot(GuiResourceId robotId);
+
private:
ResourceManager *_resMan;
SegManager *_segMan;
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 6fe472f5fa..5c17f76558 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -30,12 +30,15 @@
#include "sci/sci.h"
#include "sci/engine/state.h"
-#include "sci/graphics/screen.h"
+#include "sci/graphics/cache.h"
+#include "sci/graphics/maciconbar.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/screen.h"
+#include "sci/graphics/view.h"
namespace Sci {
-GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetPalette)
+GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMerging)
: _resMan(resMan), _screen(screen) {
int16 color;
@@ -56,23 +59,41 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetP
_sysPalette.colors[255].b = 255;
_sysPaletteChanged = false;
- if (autoSetPalette) {
- if (_resMan->getViewType() == kViewEga)
- setEGA();
- else if (_resMan->isAmiga32color())
- setAmiga();
- else
- kernelSetFromResource(999, true);
- }
+
+ // Quest for Glory 3 demo, Eco Quest 1 demo, Laura Bow 2 demo, Police Quest
+ // 1 vga and all Nick's Picks all use the older palette format and thus are
+ // not using the SCI1.1 palette merging (copying over all the colors) but
+ // the real merging done in earlier games. If we use the copying over, we
+ // will get issues because some views have marked all colors as being used
+ // and those will overwrite the current palette in that case
+ _useMerging = useMerging;
+
+ palVaryInit();
}
GfxPalette::~GfxPalette() {
+ if (_palVaryResourceId != -1)
+ palVaryRemoveTimer();
+}
+
+bool GfxPalette::isMerging() {
+ return _useMerging;
+}
+
+// meant to get called only once during init of engine
+void GfxPalette::setDefault() {
+ if (_resMan->getViewType() == kViewEga)
+ setEGA();
+ else if (_resMan->isAmiga32color())
+ setAmiga();
+ else
+ kernelSetFromResource(999, true);
}
#define SCI_PAL_FORMAT_CONSTANT 1
#define SCI_PAL_FORMAT_VARIABLE 0
-void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
+void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) {
int palFormat = 0;
int palOffset = 0;
int palColorStart = 0;
@@ -80,10 +101,18 @@ void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
int colorNo = 0;
memset(paletteOut, 0, sizeof(Palette));
- // Setup default mapping
+ // Setup 1:1 mapping
for (colorNo = 0; colorNo < 256; colorNo++) {
paletteOut->mapping[colorNo] = colorNo;
}
+ if (bytesLeft < 37) {
+ // This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full
+ // palette
+ debugC(2, "GfxPalette::createFromData() - not enough bytes in resource (%d), expected palette header", bytesLeft);
+ return;
+ }
+ // palette formats in here are not really version exclusive, we can not use sci-version to differentiate between them
+ // they were just called that way, because they started appearing in sci1.1 for example
if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0)) {
// SCI0/SCI1 palette
palFormat = SCI_PAL_FORMAT_VARIABLE; // CONSTANT;
@@ -98,6 +127,11 @@ void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
}
switch (palFormat) {
case SCI_PAL_FORMAT_CONSTANT:
+ // Check, if enough bytes left
+ if (bytesLeft < palOffset + (3 * palColorCount)) {
+ warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
+ return;
+ }
for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
paletteOut->colors[colorNo].used = 1;
paletteOut->colors[colorNo].r = data[palOffset++];
@@ -106,6 +140,10 @@ void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
}
break;
case SCI_PAL_FORMAT_VARIABLE:
+ if (bytesLeft < palOffset + (4 * palColorCount)) {
+ warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
+ return;
+ }
for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
paletteOut->colors[colorNo].used = data[palOffset++];
paletteOut->colors[colorNo].r = data[palOffset++];
@@ -179,112 +217,144 @@ void GfxPalette::setEGA() {
// Now setting colors 16-254 to the correct mix colors that occur when not doing a dithering run on
// finished pictures
for (curColor = 0x10; curColor <= 0xFE; curColor++) {
- _sysPalette.colors[curColor].used = curColor;
+ _sysPalette.colors[curColor].used = 1;
color1 = curColor & 0x0F; color2 = curColor >> 4;
_sysPalette.colors[curColor].r = (_sysPalette.colors[color1].r >> 1) + (_sysPalette.colors[color2].r >> 1);
_sysPalette.colors[curColor].g = (_sysPalette.colors[color1].g >> 1) + (_sysPalette.colors[color2].g >> 1);
_sysPalette.colors[curColor].b = (_sysPalette.colors[color1].b >> 1) + (_sysPalette.colors[color2].b >> 1);
}
+ _sysPalette.timestamp = 1;
setOnScreen();
}
-void GfxPalette::set(Palette *sciPal, bool force, bool forceRealMerge) {
+void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) {
uint32 systime = _sysPalette.timestamp;
- if (force || sciPal->timestamp != systime) {
- _sysPaletteChanged |= merge(sciPal, &_sysPalette, force, forceRealMerge);
- sciPal->timestamp = _sysPalette.timestamp;
- if (_sysPaletteChanged && _screen->_picNotValid == 0) { // && systime != _sysPalette.timestamp) {
- // Removed timestamp checking, because this shouldnt be needed anymore. I'm leaving it commented just in
- // case this causes regressions
+ if (force || newPalette->timestamp != systime) {
+ // SCI1.1+ doesnt do real merging anymore, but simply copying over the used colors from other palettes
+ // There are some games with inbetween SCI1.1 interpreters, use real merging for them (e.g. laura bow 2 demo)
+ if ((forceRealMerge) || (_useMerging))
+ _sysPaletteChanged |= merge(newPalette, force, forceRealMerge);
+ else
+ _sysPaletteChanged |= insert(newPalette, &_sysPalette);
+
+ // Adjust timestamp on newPalette, so it wont get merged/inserted w/o need
+ newPalette->timestamp = _sysPalette.timestamp;
+
+ bool updatePalette = _sysPaletteChanged && _screen->_picNotValid == 0;
+
+ if (_palVaryResourceId != -1) {
+ // Pal-vary currently active, we don't set at any time, but also insert into origin palette
+ insert(newPalette, &_palVaryOriginPalette);
+ palVaryProcess(0, updatePalette);
+ return;
+ }
+
+ if (updatePalette) {
setOnScreen();
_sysPaletteChanged = false;
}
}
}
-bool GfxPalette::merge(Palette *pFrom, Palette *pTo, bool force, bool forceRealMerge) {
+bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) {
+ bool paletteChanged = false;
+
+ for (int i = 1; i < 255; i++) {
+ if (newPalette->colors[i].used) {
+ if ((newPalette->colors[i].r != destPalette->colors[i].r) || (newPalette->colors[i].g != destPalette->colors[i].g) || (newPalette->colors[i].b != destPalette->colors[i].b)) {
+ destPalette->colors[i].r = newPalette->colors[i].r;
+ destPalette->colors[i].g = newPalette->colors[i].g;
+ destPalette->colors[i].b = newPalette->colors[i].b;
+ paletteChanged = true;
+ }
+ destPalette->colors[i].used = newPalette->colors[i].used;
+ newPalette->mapping[i] = i;
+ }
+ }
+ // We don't update the timestamp for SCI1.1, it's only updated on kDrawPic calls
+ return paletteChanged;
+}
+
+bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
uint16 res;
int i,j;
bool paletteChanged = false;
- if ((!forceRealMerge) && (getSciVersion() >= SCI_VERSION_1_1)) {
- // SCI1.1+ doesnt do real merging anymore, but simply copying over the used colors from other palettes
- for (i = 1; i < 255; i++) {
- if (pFrom->colors[i].used) {
- if ((pFrom->colors[i].r != pTo->colors[i].r) || (pFrom->colors[i].g != pTo->colors[i].g) || (pFrom->colors[i].b != pTo->colors[i].b)) {
- pTo->colors[i].r = pFrom->colors[i].r;
- pTo->colors[i].g = pFrom->colors[i].g;
- pTo->colors[i].b = pFrom->colors[i].b;
- paletteChanged = true;
- }
- pTo->colors[i].used = pFrom->colors[i].used;
- pFrom->mapping[i] = i;
+ // colors 0 (black) and 255 (white) are not affected by merging
+ for (i = 1 ; i < 255; i++) {
+ if (!newPalette->colors[i].used)// color is not used - so skip it
+ continue;
+ // forced palette merging or dest color is not used yet
+ if (force || (!_sysPalette.colors[i].used)) {
+ _sysPalette.colors[i].used = newPalette->colors[i].used;
+ if ((newPalette->colors[i].r != _sysPalette.colors[i].r) || (newPalette->colors[i].g != _sysPalette.colors[i].g) || (newPalette->colors[i].b != _sysPalette.colors[i].b)) {
+ _sysPalette.colors[i].r = newPalette->colors[i].r;
+ _sysPalette.colors[i].g = newPalette->colors[i].g;
+ _sysPalette.colors[i].b = newPalette->colors[i].b;
+ paletteChanged = true;
}
+ newPalette->mapping[i] = i;
+ continue;
}
- } else {
- // colors 0 (black) and 255 (white) are not affected by merging
- for (i = 1 ; i < 255; i++) {
- if (!pFrom->colors[i].used)// color is not used - so skip it
- continue;
- // forced palette merging or dest color is not used yet
- if (force || (!pTo->colors[i].used)) {
- pTo->colors[i].used = pFrom->colors[i].used;
- if ((pFrom->colors[i].r != pTo->colors[i].r) || (pFrom->colors[i].g != pTo->colors[i].g) || (pFrom->colors[i].b != pTo->colors[i].b)) {
- pTo->colors[i].r = pFrom->colors[i].r;
- pTo->colors[i].g = pFrom->colors[i].g;
- pTo->colors[i].b = pFrom->colors[i].b;
- paletteChanged = true;
- }
- pFrom->mapping[i] = i;
- continue;
- }
- // is the same color already at the same position? -> match it directly w/o lookup
- // this fixes games like lsl1demo/sq5 where the same rgb color exists multiple times and where we would
- // otherwise match the wrong one (which would result into the pixels affected (or not) by palette changes)
- if ((pTo->colors[i].r == pFrom->colors[i].r) && (pTo->colors[i].g == pFrom->colors[i].g) && (pTo->colors[i].b == pFrom->colors[i].b)) {
- pFrom->mapping[i] = i;
- continue;
- }
- // check if exact color could be matched
- res = matchColor(pTo, pFrom->colors[i].r, pFrom->colors[i].g, pFrom->colors[i].b);
- if (res & 0x8000) { // exact match was found
- pFrom->mapping[i] = res & 0xFF;
- continue;
- }
- // no exact match - see if there is an unused color
- for (j = 1; j < 256; j++)
- if (!pTo->colors[j].used) {
- pTo->colors[j].used = pFrom->colors[i].used;
- pTo->colors[j].r = pFrom->colors[i].r;
- pTo->colors[j].g = pFrom->colors[i].g;
- pTo->colors[j].b = pFrom->colors[i].b;
- pFrom->mapping[i] = j;
- paletteChanged = true;
- break;
- }
- // if still no luck - set an approximate color
- if (j == 256) {
- pFrom->mapping[i] = res & 0xFF;
- pTo->colors[res & 0xFF].used |= 0x10;
+ // is the same color already at the same position? -> match it directly w/o lookup
+ // this fixes games like lsl1demo/sq5 where the same rgb color exists multiple times and where we would
+ // otherwise match the wrong one (which would result into the pixels affected (or not) by palette changes)
+ if ((_sysPalette.colors[i].r == newPalette->colors[i].r) && (_sysPalette.colors[i].g == newPalette->colors[i].g) && (_sysPalette.colors[i].b == newPalette->colors[i].b)) {
+ newPalette->mapping[i] = i;
+ continue;
+ }
+ // check if exact color could be matched
+ res = matchColor(newPalette->colors[i].r, newPalette->colors[i].g, newPalette->colors[i].b);
+ if (res & 0x8000) { // exact match was found
+ newPalette->mapping[i] = res & 0xFF;
+ continue;
+ }
+ // no exact match - see if there is an unused color
+ for (j = 1; j < 256; j++)
+ if (!_sysPalette.colors[j].used) {
+ _sysPalette.colors[j].used = newPalette->colors[i].used;
+ _sysPalette.colors[j].r = newPalette->colors[i].r;
+ _sysPalette.colors[j].g = newPalette->colors[i].g;
+ _sysPalette.colors[j].b = newPalette->colors[i].b;
+ newPalette->mapping[i] = j;
+ paletteChanged = true;
+ break;
}
+ // if still no luck - set an approximate color
+ if (j == 256) {
+ newPalette->mapping[i] = res & 0xFF;
+ _sysPalette.colors[res & 0xFF].used |= 0x10;
}
}
- pTo->timestamp = g_system->getMillis() * 60 / 1000;
+
+ if (!forceRealMerge)
+ _sysPalette.timestamp = g_system->getMillis() * 60 / 1000;
return paletteChanged;
}
-uint16 GfxPalette::matchColor(Palette *pPal, byte r, byte g, byte b) {
+// This is called for SCI1.1, when kDrawPic got done. We update sysPalette timestamp this way for SCI1.1 and also load
+// target-palette, if palvary is active
+void GfxPalette::drewPicture(GuiResourceId pictureId) {
+ if (!_useMerging) // Don't do this on inbetween SCI1.1 games
+ _sysPalette.timestamp++;
+
+ if (_palVaryResourceId != -1) {
+ palVaryLoadTargetPalette(pictureId);
+ }
+}
+
+uint16 GfxPalette::matchColor(byte r, byte g, byte b) {
byte found = 0xFF;
int diff = 0x2FFFF, cdiff;
int16 dr,dg,db;
for (int i = 1; i < 255; i++) {
- if ((!pPal->colors[i].used))
+ if ((!_sysPalette.colors[i].used))
continue;
- dr = pPal->colors[i].r - r;
- dg = pPal->colors[i].g - g;
- db = pPal->colors[i].b - b;
+ dr = _sysPalette.colors[i].r - r;
+ dg = _sysPalette.colors[i].g - g;
+ db = _sysPalette.colors[i].b - b;
// minimum squares match
cdiff = (dr*dr) + (dg*dg) + (db*db);
// minimum sum match (Sierra's)
@@ -305,20 +375,22 @@ void GfxPalette::getSys(Palette *pal) {
}
void GfxPalette::setOnScreen() {
-// if (pal != &_sysPalette)
-// memcpy(&_sysPalette,pal,sizeof(Palette));
// We dont change palette at all times for amiga
if (_resMan->isAmiga32color())
return;
_screen->setPalette(&_sysPalette);
+
+ // Redraw the Mac SCI1.1 Icon bar every palette change
+ if (g_sci->_gfxMacIconBar)
+ g_sci->_gfxMacIconBar->drawIcons();
}
bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) {
- Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), 0);
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
Palette palette;
if (palResource) {
- createFromData(palResource->data, &palette);
+ createFromData(palResource->data, palResource->size, &palette);
set(&palette, force);
return true;
}
@@ -341,12 +413,18 @@ void GfxPalette::kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag)
void GfxPalette::kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette) {
memset(&_sysPalette.intensity[0] + fromColor, intensity, toColor - fromColor);
- if (setPalette)
+ if (setPalette) {
setOnScreen();
+ EngineState *state = g_sci->getEngineState();
+ // Call speed throttler from here as well just in case we need it
+ // At least in kq6 intro the scripts call us in a tight loop for fadein/fadeout
+ state->speedThrottler(30);
+ state->_throttleTrigger = true;
+ }
}
int16 GfxPalette::kernelFindColor(uint16 r, uint16 g, uint16 b) {
- return matchColor(&_sysPalette, r, g, b) & 0xFF;
+ return matchColor(r, g, b) & 0xFF;
}
// Returns true, if palette got changed
@@ -373,6 +451,8 @@ bool GfxPalette::kernelAnimate(byte fromColor, byte toColor, int speed) {
scheduleCount++;
}
+ g_sci->getEngineState()->_throttleTrigger = true;
+
for (scheduleNr = 0; scheduleNr < scheduleCount; scheduleNr++) {
if (_schedules[scheduleNr].from == fromColor) {
if (_schedules[scheduleNr].schedule <= now) {
@@ -408,8 +488,58 @@ void GfxPalette::kernelAnimateSet() {
setOnScreen();
}
+reg_t GfxPalette::kernelSave() {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+ reg_t memoryId = segMan->allocateHunkEntry("kPalette(save)", 1024);
+ byte *memoryPtr = segMan->getHunkPointer(memoryId);
+ if (memoryPtr) {
+ for (int colorNr = 0; colorNr < 256; colorNr++) {
+ *memoryPtr++ = _sysPalette.colors[colorNr].used;
+ *memoryPtr++ = _sysPalette.colors[colorNr].r;
+ *memoryPtr++ = _sysPalette.colors[colorNr].g;
+ *memoryPtr++ = _sysPalette.colors[colorNr].b;
+ }
+ }
+ return memoryId;
+}
+
+void GfxPalette::kernelRestore(reg_t memoryHandle) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+ if (!memoryHandle.isNull()) {
+ byte *memoryPtr = segMan->getHunkPointer(memoryHandle);
+ if (!memoryPtr)
+ error("Bad handle used for kPalette(restore)");
+
+ Palette restoredPalette;
+
+ restoredPalette.timestamp = 0;
+ for (int colorNr = 0; colorNr < 256; colorNr++) {
+ restoredPalette.colors[colorNr].used = *memoryPtr++;
+ restoredPalette.colors[colorNr].r = *memoryPtr++;
+ restoredPalette.colors[colorNr].g = *memoryPtr++;
+ restoredPalette.colors[colorNr].b = *memoryPtr++;
+ }
+
+ set(&restoredPalette, true);
+ }
+}
+
void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) {
- warning("kAssertPalette %d", resourceId);
+ // Sometimes invalid viewIds are asked for, ignore those (e.g. qfg1vga)
+ //if (!_resMan->testResource(ResourceId(kResourceTypeView, resourceId)))
+ // return;
+ // maybe we took the wrong parameter before, if this causes invalid view again, enable to commented out code again
+
+ GfxView *view = g_sci->_gfxCache->getView(resourceId);
+ Palette *viewPalette = view->getPalette();
+ if (viewPalette) {
+ // merge/insert this palette
+ set(viewPalette, true);
+ }
+}
+
+void GfxPalette::kernelSyncScreenPalette() {
+ _screen->getPalette(&_sysPalette);
}
// palVary
@@ -438,40 +568,191 @@ void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) {
// Saving/restoring
// need to save start and target-palette, when palVaryOn = true
-void GfxPalette::startPalVary(uint16 paletteId, uint16 ticks) {
- kernelSetFromResource(paletteId, true);
- return;
+void GfxPalette::palVaryInit() {
+ _palVaryResourceId = -1;
+ _palVaryPaused = 0;
+ _palVarySignal = 0;
+ _palVaryStep = 0;
+ _palVaryStepStop = 0;
+ _palVaryDirection = 0;
+ _palVaryTicks = 0;
+}
- if (_palVaryId >= 0) // another palvary is taking place, return
- return;
+bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) {
+ _palVaryResourceId = resourceId;
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
+ if (palResource) {
+ // Load and initialize destination palette
+ createFromData(palResource->data, palResource->size, &_palVaryTargetPalette);
+ return true;
+ }
+ return false;
+}
+
+void GfxPalette::palVaryInstallTimer() {
+ int16 ticks = _palVaryTicks > 0 ? _palVaryTicks : 1;
+ // Call signal increase every [ticks]
+ g_sci->getTimerManager()->installTimerProc(&palVaryCallback, 1000000 / 60 * ticks, this);
+}
+
+void GfxPalette::palVaryRemoveTimer() {
+ g_sci->getTimerManager()->removeTimerProc(&palVaryCallback);
+}
+
+bool GfxPalette::kernelPalVaryInit(GuiResourceId resourceId, uint16 ticks, uint16 stepStop, uint16 direction) {
+ if (_palVaryResourceId != -1) // another palvary is taking place, return
+ return false;
+
+ if (palVaryLoadTargetPalette(resourceId)) {
+ // Save current palette
+ memcpy(&_palVaryOriginPalette, &_sysPalette, sizeof(Palette));
+
+ _palVarySignal = 0;
+ _palVaryTicks = ticks;
+ _palVaryStep = 1;
+ _palVaryStepStop = stepStop;
+ _palVaryDirection = direction;
+ // if no ticks are given, jump directly to destination
+ if (!_palVaryTicks)
+ _palVaryDirection = stepStop;
+ palVaryInstallTimer();
+ return true;
+ }
+ return false;
+}
+
+int16 GfxPalette::kernelPalVaryReverse(int16 ticks, uint16 stepStop, int16 direction) {
+ if (_palVaryResourceId == -1)
+ return 0;
+
+ if (_palVaryStep > 64)
+ _palVaryStep = 64;
+ if (ticks != -1)
+ _palVaryTicks = ticks;
+ _palVaryStepStop = stepStop;
+ _palVaryDirection = direction != -1 ? -direction : -_palVaryDirection;
+
+ if (!_palVaryTicks)
+ _palVaryDirection = _palVaryStepStop - _palVaryStep;
+ palVaryInstallTimer();
+ return kernelPalVaryGetCurrentStep();
+}
+
+int16 GfxPalette::kernelPalVaryGetCurrentStep() {
+ if (_palVaryDirection >= 0)
+ return _palVaryStep;
+ return -_palVaryStep;
+}
+
+int16 GfxPalette::kernelPalVaryChangeTarget(GuiResourceId resourceId) {
+ if (_palVaryResourceId != -1) {
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
+ if (palResource) {
+ Palette insertPalette;
+ createFromData(palResource->data, palResource->size, &insertPalette);
+ // insert new palette into target
+ insert(&insertPalette, &_palVaryTargetPalette);
+ // update palette and set on screen
+ palVaryProcess(0, true);
+ }
+ }
+ return kernelPalVaryGetCurrentStep();
+}
- _palVaryId = paletteId;
- _palVaryStart = g_system->getMillis();
- _palVaryEnd = _palVaryStart + ticks * 1000 / 60;
- g_sci->getTimerManager()->installTimerProc(&palVaryCallback, 1000 / 60, this);
+void GfxPalette::kernelPalVaryChangeTicks(uint16 ticks) {
+ _palVaryTicks = ticks;
+ if (_palVaryStep - _palVaryStepStop) {
+ palVaryRemoveTimer();
+ palVaryInstallTimer();
+ }
}
-void GfxPalette::togglePalVary(bool pause) {
+void GfxPalette::kernelPalVaryPause(bool pause) {
+ if (_palVaryResourceId == -1)
+ return;
// this call is actually counting states, so calling this 3 times with true will require calling it later
// 3 times with false to actually remove pause
-
- // TODO
+ if (pause) {
+ _palVaryPaused++;
+ } else {
+ if (_palVaryPaused)
+ _palVaryPaused--;
+ }
}
-void GfxPalette::stopPalVary() {
- g_sci->getTimerManager()->removeTimerProc(&palVaryCallback);
- _palVaryId = -1; // invalidate the target palette
+void GfxPalette::kernelPalVaryDeinit() {
+ palVaryRemoveTimer();
- // HACK: just set the target palette
- kernelSetFromResource(_palVaryId, true);
+ _palVaryResourceId = -1; // invalidate the target palette
}
void GfxPalette::palVaryCallback(void *refCon) {
- ((GfxPalette *)refCon)->doPalVary();
+ ((GfxPalette *)refCon)->palVaryIncreaseSignal();
+}
+
+void GfxPalette::palVaryIncreaseSignal() {
+ if (!_palVaryPaused)
+ _palVarySignal++;
}
-void GfxPalette::doPalVary() {
- // TODO: do palette transition here...
+// Actually do the pal vary processing
+void GfxPalette::palVaryUpdate() {
+ if (_palVarySignal) {
+ palVaryProcess(_palVarySignal, true);
+ _palVarySignal = 0;
+ }
+}
+
+void GfxPalette::palVaryPrepareForTransition() {
+ if (_palVaryResourceId != -1) {
+ // Before doing transitions, we have to prepare palette
+ palVaryProcess(0, false);
+ }
+}
+
+// Processes pal vary updates
+void GfxPalette::palVaryProcess(int signal, bool setPalette) {
+ int16 stepChange = signal * _palVaryDirection;
+
+ _palVaryStep += stepChange;
+ if (stepChange > 0) {
+ if (_palVaryStep > _palVaryStepStop)
+ _palVaryStep = _palVaryStepStop;
+ } else {
+ if (_palVaryStep < _palVaryStepStop) {
+ if (signal)
+ _palVaryStep = _palVaryStepStop;
+ }
+ }
+
+ // We don't need updates anymore, if we reached end-position
+ if (_palVaryStep == _palVaryStepStop)
+ palVaryRemoveTimer();
+ if (_palVaryStep == 0)
+ _palVaryResourceId = -1;
+
+ // Calculate inbetween palette
+ Sci::Color inbetween;
+ int16 color;
+ for (int colorNr = 1; colorNr < 255; colorNr++) {
+ inbetween.used = _sysPalette.colors[colorNr].used;
+ color = _palVaryTargetPalette.colors[colorNr].r - _palVaryOriginPalette.colors[colorNr].r;
+ inbetween.r = ((color * _palVaryStep) / 64) + _palVaryOriginPalette.colors[colorNr].r;
+ color = _palVaryTargetPalette.colors[colorNr].g - _palVaryOriginPalette.colors[colorNr].g;
+ inbetween.g = ((color * _palVaryStep) / 64) + _palVaryOriginPalette.colors[colorNr].g;
+ color = _palVaryTargetPalette.colors[colorNr].b - _palVaryOriginPalette.colors[colorNr].b;
+ inbetween.b = ((color * _palVaryStep) / 64) + _palVaryOriginPalette.colors[colorNr].b;
+
+ if (memcmp(&inbetween, &_sysPalette.colors[colorNr], sizeof(Sci::Color))) {
+ _sysPalette.colors[colorNr] = inbetween;
+ _sysPaletteChanged = true;
+ }
+ }
+
+ if ((_sysPaletteChanged) && (setPalette) && (_screen->_picNotValid == 0)) {
+ setOnScreen();
+ _sysPaletteChanged = false;
+ }
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 46fec48739..e10c10718d 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -34,22 +34,28 @@ class Screen;
/**
* Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes
*/
-class GfxPalette {
+class GfxPalette : public Common::Serializable {
public:
- GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetPalette = true);
+ GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMerging);
~GfxPalette();
- void createFromData(byte *data, Palette *paletteOut);
+ bool isMerging();
+
+ void setDefault();
+ void createFromData(byte *data, int bytesLeft, Palette *paletteOut);
bool setAmiga();
void modifyAmigaPalette(byte *data);
void setEGA();
void set(Palette *sciPal, bool force, bool forceRealMerge = false);
- bool merge(Palette *pFrom, Palette *pTo, bool force, bool forceRealMerge);
- uint16 matchColor(Palette *pPal, byte r, byte g, byte b);
+ bool insert(Palette *newPalette, Palette *destPalette);
+ bool merge(Palette *pFrom, bool force, bool forceRealMerge);
+ uint16 matchColor(byte r, byte g, byte b);
void getSys(Palette *pal);
void setOnScreen();
+ void drewPicture(GuiResourceId pictureId);
+
bool kernelSetFromResource(GuiResourceId resourceId, bool force);
void kernelSetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
void kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
@@ -57,27 +63,53 @@ public:
int16 kernelFindColor(uint16 r, uint16 g, uint16 b);
bool kernelAnimate(byte fromColor, byte toColor, int speed);
void kernelAnimateSet();
+ reg_t kernelSave();
+ void kernelRestore(reg_t memoryHandle);
void kernelAssertPalette(GuiResourceId resourceId);
- void startPalVary(uint16 paletteId, uint16 ticks);
- void togglePalVary(bool pause);
- void stopPalVary();
+ void kernelSyncScreenPalette();
+
+ bool kernelPalVaryInit(GuiResourceId resourceId, uint16 ticks, uint16 stepStop, uint16 direction);
+ int16 kernelPalVaryReverse(int16 ticks, uint16 stepStop, int16 direction);
+ int16 kernelPalVaryGetCurrentStep();
+ int16 kernelPalVaryChangeTarget(GuiResourceId resourceId);
+ void kernelPalVaryChangeTicks(uint16 ticks);
+ void kernelPalVaryPause(bool pause);
+ void kernelPalVaryDeinit();
+ void palVaryUpdate();
+ void palVaryPrepareForTransition();
+ void palVaryProcess(int signal, bool setPalette);
Palette _sysPalette;
+ virtual void saveLoadWithSerializer(Common::Serializer &s);
+ void palVarySaveLoadPalette(Common::Serializer &s, Palette *palette);
+
private:
+ void palVaryInit();
+ void palVaryInstallTimer();
+ void palVaryRemoveTimer();
+ bool palVaryLoadTargetPalette(GuiResourceId resourceId);
static void palVaryCallback(void *refCon);
- void doPalVary();
+ void palVaryIncreaseSignal();
GfxScreen *_screen;
ResourceManager *_resMan;
- int16 _palVaryId;
- uint32 _palVaryStart;
- uint32 _palVaryEnd;
bool _sysPaletteChanged;
+ bool _useMerging;
Common::Array<PalSchedule> _schedules;
+
+ GuiResourceId _palVaryResourceId;
+ Palette _palVaryOriginPalette;
+ Palette _palVaryTargetPalette;
+ int16 _palVaryStep;
+ int16 _palVaryStepStop;
+ int16 _palVaryDirection;
+ uint16 _palVaryTicks;
+ int _palVaryPaused;
+ int _palVarySignal;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 74f651a88a..e568316919 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -43,10 +43,11 @@ GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster,
}
GfxPicture::~GfxPicture() {
+ _resMan->unlockResource(_resource);
}
void GfxPicture::initData(GuiResourceId resourceId) {
- _resource = _resMan->findResource(ResourceId(kResourceTypePic, resourceId), false);
+ _resource = _resMan->findResource(ResourceId(kResourceTypePic, resourceId), true);
if (!_resource) {
error("picture resource %d not found", resourceId);
}
@@ -56,7 +57,9 @@ GuiResourceId GfxPicture::getResourceId() {
return _resourceId;
}
-// TODO: subclass this
+// differentiation between various picture formats can NOT get done using sci-version checks.
+// Games like PQ1 use the "old" vector data picture format, but are actually SCI1.1
+// We should leave this that way to decide the format on-the-fly instead of hardcoding it in any way
void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) {
uint16 headerSize;
@@ -69,15 +72,18 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1
headerSize = READ_LE_UINT16(_resource->data);
switch (headerSize) {
case 0x26: // SCI 1.1 VGA picture
+ _resourceType = SCI_PICTURE_TYPE_SCI11;
drawSci11Vga();
break;
#ifdef ENABLE_SCI32
case 0x0e: // SCI32 VGA picture
- drawSci32Vga();
+ _resourceType = SCI_PICTURE_TYPE_SCI32;
+ //drawSci32Vga();
break;
#endif
default:
// VGA, EGA or Amiga vector data
+ _resourceType = SCI_PICTURE_TYPE_REGULAR;
drawVectorData(_resource->data, _resource->size);
}
}
@@ -94,29 +100,46 @@ void GfxPicture::reset() {
void GfxPicture::drawSci11Vga() {
byte *inbuffer = _resource->data;
int size = _resource->size;
- int has_cel = READ_LE_UINT16(inbuffer + 4);
- int vector_dataPos = READ_LE_UINT16(inbuffer + 16);
+ int priorityBandsCount = inbuffer[3];
+ int has_cel = inbuffer[4];
+ int vector_dataPos = READ_LE_UINT32(inbuffer + 16);
int vector_size = size - vector_dataPos;
- int palette_data_ptr = READ_LE_UINT16(inbuffer + 28);
- int cel_headerPos = READ_LE_UINT16(inbuffer + 32);
- int cel_RlePos = READ_LE_UINT16(inbuffer + cel_headerPos + 24);
- int cel_LiteralPos = READ_LE_UINT16(inbuffer + cel_headerPos + 28);
+ int palette_data_ptr = READ_LE_UINT32(inbuffer + 28);
+ int cel_headerPos = READ_LE_UINT32(inbuffer + 32);
+ int cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24);
+ int cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28);
Palette palette;
+ // Header
+ // [headerSize:WORD] [unknown:BYTE] [priorityBandCount:BYTE] [hasCel:BYTE] [unknown:BYTE]
+ // [unknown:WORD] [unknown:WORD] [unknown:WORD] [unknown:WORD] [unknown:WORD]
+ // Offset 16
+ // [vectorDataOffset:DWORD] [unknown:DWORD] [unknown:DWORD] [paletteDataOffset:DWORD]
+ // Offset 32
+ // [celHeaderOffset:DWORD] [unknown:DWORD]
+ // [priorityBandData:WORD] * priorityBandCount
+ // [priority:BYTE] [unknown:BYTE]
+
// Create palette and set it
- _palette->createFromData(inbuffer + palette_data_ptr, &palette);
+ _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
_palette->set(&palette, true);
- // display Cel-data
- if (has_cel) {
- drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, false);
+ // priority bands are supposed to be 14 for sci1.1 pictures
+ assert(priorityBandsCount == 14);
+
+ if (_addToFlag) {
+ _priority = inbuffer[40 + priorityBandsCount * 2] & 0xF;
}
+ // display Cel-data
+ if (has_cel)
+ drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, 0);
+
// process vector data
drawVectorData(inbuffer + vector_dataPos, vector_size);
- // Remember priority band information for later
- _ports->priorityBandsRemember(inbuffer + 40);
+ // Set priority band information
+ _ports->priorityBandsInitSci11(inbuffer + 40);
}
#ifdef ENABLE_SCI32
@@ -125,44 +148,80 @@ int16 GfxPicture::getSci32celCount() {
return inbuffer[2];
}
-void GfxPicture::drawSci32Vga(int16 celNo) {
+int16 GfxPicture::getSci32celY(int16 celNo) {
+ byte *inbuffer = _resource->data;
+ int header_size = READ_LE_UINT16(inbuffer);
+ int cel_headerPos = header_size + 42 * celNo;
+ return READ_LE_UINT16(inbuffer + cel_headerPos + 40);
+}
+
+int16 GfxPicture::getSci32celX(int16 celNo) {
+ byte *inbuffer = _resource->data;
+ int header_size = READ_LE_UINT16(inbuffer);
+ int cel_headerPos = header_size + 42 * celNo;
+ return READ_LE_UINT16(inbuffer + cel_headerPos + 38);
+}
+
+int16 GfxPicture::getSci32celWidth(int16 celNo) {
+ byte *inbuffer = _resource->data;
+ int header_size = READ_LE_UINT16(inbuffer);
+ int cel_headerPos = header_size + 42 * celNo;
+ return READ_LE_UINT16(inbuffer + cel_headerPos + 0);
+}
+
+int16 GfxPicture::getSci32celPriority(int16 celNo) {
+ byte *inbuffer = _resource->data;
+ int header_size = READ_LE_UINT16(inbuffer);
+ int cel_headerPos = header_size + 42 * celNo;
+ return READ_LE_UINT16(inbuffer + cel_headerPos + 36);
+}
+
+void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictureX, bool mirrored) {
byte *inbuffer = _resource->data;
int size = _resource->size;
int header_size = READ_LE_UINT16(inbuffer);
int palette_data_ptr = READ_LE_UINT16(inbuffer + 6);
- int celCount = inbuffer[2];
+// int celCount = inbuffer[2];
int cel_headerPos = header_size;
int cel_RlePos, cel_LiteralPos;
- int cel_relXpos, cel_relYpos;
Palette palette;
// HACK
- _mirroredFlag = false;
+ _mirroredFlag = mirrored;
_addToFlag = false;
+ _resourceType = SCI_PICTURE_TYPE_SCI32;
- if ((celNo == -1) || (celNo == 0)) {
+ if (celNo == 0) {
// Create palette and set it
- _palette->createFromData(inbuffer + palette_data_ptr, &palette);
+ _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
_palette->set(&palette, true);
}
- if (celNo != -1) {
- cel_headerPos += 42 * celNo;
- celCount = 1;
- }
- while (celCount > 0) {
- cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24);
- cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28);
- cel_relXpos = READ_LE_UINT16(inbuffer + cel_headerPos + 38);
- cel_relYpos = READ_LE_UINT16(inbuffer + cel_headerPos + 40);
- drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos, true);
- cel_headerPos += 42;
- celCount--;
+ // Header
+ // [headerSize:WORD] [celCount:BYTE] [Unknown:BYTE] [Unknown:WORD] [paletteOffset:DWORD] [Unknown:DWORD]
+ // cel-header follow afterwards, each is 42 bytes
+ // Cel-Header
+ // [width:WORD] [height:WORD] [displaceX:WORD] [displaceY:WORD] [clearColor:BYTE] [compressed:BYTE]
+ // offset 10-23 is unknown
+ // [rleOffset:DWORD] [literalOffset:DWORD] [Unknown:WORD] [Unknown:WORD] [priority:WORD] [relativeXpos:WORD] [relativeYpos:WORD]
+
+ cel_headerPos += 42 * celNo;
+
+ if (mirrored) {
+ // switch around relativeXpos
+ Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea();
+ drawX = displayArea.width() - drawX - READ_LE_UINT16(inbuffer + cel_headerPos + 0);
}
+
+ cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24);
+ cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28);
+
+ drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, drawX, drawY, pictureX);
+ cel_headerPos += 42;
}
#endif
-void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header) {
+void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) {
byte *celBitmap = NULL;
byte *ptr = NULL;
byte *headerPtr = inbuffer + headerPos;
@@ -179,11 +238,16 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
int pixelNr, pixelCount;
#ifdef ENABLE_SCI32
- if (!hasSci32Header) {
+ if (_resourceType != SCI_PICTURE_TYPE_SCI32) {
#endif
displaceX = (signed char)headerPtr[4];
displaceY = (unsigned char)headerPtr[5];
- clearColor = headerPtr[6];
+ if (_resourceType == SCI_PICTURE_TYPE_SCI11) {
+ // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise
+ clearColor = _screen->getColorWhite();
+ } else {
+ clearColor = headerPtr[6];
+ }
#ifdef ENABLE_SCI32
} else {
displaceX = READ_LE_UINT16(headerPtr + 4); // probably signed?!?
@@ -285,52 +349,71 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea();
- y = callerY + displayArea.top;
- lastY = MIN<int16>(height + y, displayArea.bottom);
- leftX = callerX + displayArea.left;
- rightX = MIN<int16>(width + leftX, displayArea.right);
-
- // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen
- // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all)
- // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint
- // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra
- if (!_addToFlag)
- clearColor = _screen->getColorWhite();
-
- ptr = celBitmap;
- if (!_mirroredFlag) {
- // Draw bitmap to screen
- x = leftX;
- while (y < lastY) {
- curByte = *ptr++;
- if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y)))
- _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY, curByte, priority, 0);
-
- x++;
-
- if (x >= rightX) {
- if (width > rightX - leftX) // Skip extra pixels at the end of the row
- ptr += width - (rightX - leftX);
- x = leftX;
- y++;
- }
+ uint16 skipCelBitmapPixels = 0;
+ int16 displayWidth = width;
+ if (pictureX) {
+ // scroll position for picture active, we need to adjust drawX accordingly
+ drawX -= pictureX;
+ if (drawX < 0) {
+ skipCelBitmapPixels = -drawX;
+ displayWidth -= skipCelBitmapPixels;
+ drawX = 0;
}
- } else {
- // Draw bitmap to screen (mirrored)
- x = rightX - 1;
- while (y < lastY) {
- curByte = *ptr++;
- if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y)))
- _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY, curByte, priority, 0);
-
- if (x == leftX) {
- if (width > rightX - leftX) // Skip extra pixels at the end of the row
- ptr += width - (rightX - leftX);
- x = rightX;
- y++;
+ }
+
+ if (displayWidth > 0) {
+ y = displayArea.top + drawY;
+ lastY = MIN<int16>(height + y, displayArea.bottom);
+ leftX = displayArea.left + drawX;
+ rightX = MIN<int16>(displayWidth + leftX, displayArea.right);
+
+ uint16 sourcePixelSkipPerRow = 0;
+ if (width > rightX - leftX)
+ sourcePixelSkipPerRow = width - (rightX - leftX);
+
+ // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen
+ // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all)
+ // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint
+ // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra
+ if (!_addToFlag)
+ clearColor = _screen->getColorWhite();
+
+ byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY;
+
+ ptr = celBitmap;
+ ptr += skipCelBitmapPixels;
+ if (!_mirroredFlag) {
+ // Draw bitmap to screen
+ x = leftX;
+ while (y < lastY) {
+ curByte = *ptr++;
+ if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y)))
+ _screen->putPixel(x, y, drawMask, curByte, priority, 0);
+
+ x++;
+
+ if (x >= rightX) {
+ ptr += sourcePixelSkipPerRow;
+ x = leftX;
+ y++;
+ }
}
+ } else {
+ // Draw bitmap to screen (mirrored)
+ x = rightX - 1;
+ while (y < lastY) {
+ curByte = *ptr++;
+ if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y)))
+ _screen->putPixel(x, y, drawMask, curByte, priority, 0);
+
+ if (x == leftX) {
+ ptr += sourcePixelSkipPerRow;
+ x = rightX;
+ y++;
+ }
- x--;
+ x--;
+ }
}
}
@@ -427,7 +510,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
memcpy(&EGApalettes[i], &vector_defaultEGApalette, sizeof(vector_defaultEGApalette));
memcpy(&EGApriority, &vector_defaultEGApriority, sizeof(vector_defaultEGApriority));
- if (strcmp(g_sci->getGameID(), "iceman") == 0) {
+ if (g_sci->getGameId() == GID_ICEMAN) {
// WORKAROUND: we remove certain visual&priority lines in underwater rooms of iceman, when not dithering the
// picture. Normally those lines aren't shown, because they share the same color as the dithered
// fill color combination. When not dithering, those lines would appear and get distracting.
@@ -518,10 +601,30 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
+ // Pattern opcodes are handled in sierra sci1.1+ as actual NOPs and normally they definitely should not occur
+ // inside picture data for such games
case PIC_OP_SET_PATTERN:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11) {
+ if (g_sci->getGameId() == GID_SQ4) {
+ // WORKAROUND: For SQ4 / for some pictures handle this like a terminator
+ // This picture includes garbage data, first a set pattern w/o parameter and then short pattern
+ // I guess that garbage is a left over from the sq4-floppy (sci1) to sq4-cd (sci1.1) conversion
+ switch (_resourceId) {
+ case 35:
+ case 381:
+ case 376:
+ return;
+ default:
+ break;
+ }
+ }
+ error("pic-operation set pattern inside sci1.1+ vector data");
+ }
pattern_Code = data[curPos++];
break;
case PIC_OP_SHORT_PATTERNS:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
+ error("pic-operation short pattern inside sci1.1+ vector data");
vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
vectorGetAbsCoords(data, curPos, x, y);
vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
@@ -532,6 +635,8 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
case PIC_OP_MEDIUM_PATTERNS:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
+ error("pic-operation medium pattern inside sci1.1+ vector data");
vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
vectorGetAbsCoords(data, curPos, x, y);
vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
@@ -542,6 +647,8 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
case PIC_OP_ABSOLUTE_PATTERN:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
+ error("pic-operation absolute pattern inside sci1.1+ vector data");
while (vectorIsNonOpcode(data[curPos])) {
vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
vectorGetAbsCoords(data, curPos, x, y);
@@ -585,7 +692,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
vectorGetAbsCoordsNoMirror(data, curPos, x, y);
size = READ_LE_UINT16(data + curPos); curPos += 2;
_priority = pic_priority; // set global priority so the cel gets drawn using current priority as well
- drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false);
+ drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0);
curPos += size;
break;
case PIC_OPX_EGA_SET_PRIORITY_TABLE:
@@ -625,9 +732,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
vectorGetAbsCoordsNoMirror(data, curPos, x, y);
size = READ_LE_UINT16(data + curPos); curPos += 2;
_priority = pic_priority; // set global priority so the cel gets drawn using current priority as well
- if (pic_priority == 255)
- _priority = 0; // if priority not set, use priority 0
- drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false);
+ drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0);
curPos += size;
break;
case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST:
diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h
index 3374c33b52..7cd0d71b67 100644
--- a/engines/sci/graphics/picture.h
+++ b/engines/sci/graphics/picture.h
@@ -32,6 +32,12 @@ namespace Sci {
#define SCI_PATTERN_CODE_USE_TEXTURE 0x20
#define SCI_PATTERN_CODE_PENSIZE 0x07
+enum {
+ SCI_PICTURE_TYPE_REGULAR = 0,
+ SCI_PICTURE_TYPE_SCI11 = 1,
+ SCI_PICTURE_TYPE_SCI32 = 2
+};
+
class GfxPorts;
class GfxScreen;
class GfxPalette;
@@ -50,14 +56,18 @@ public:
#ifdef ENABLE_SCI32
int16 getSci32celCount();
- void drawSci32Vga(int16 celNo = -1);
+ int16 getSci32celY(int16 celNo);
+ int16 getSci32celX(int16 celNo);
+ int16 getSci32celWidth(int16 celNo);
+ int16 getSci32celPriority(int16 celNo);
+ void drawSci32Vga(int16 celNo, int16 callerX, int16 callerY, int16 pictureX, bool mirrored);
#endif
private:
void initData(GuiResourceId resourceId);
void reset();
void drawSci11Vga();
- void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header);
+ void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX);
void drawVectorData(byte *data, int size);
bool vectorIsNonOpcode(byte pixel);
void vectorGetAbsCoords(byte *data, int &curPos, int16 &x, int16 &y);
@@ -80,6 +90,7 @@ private:
int16 _resourceId;
Resource *_resource;
+ int _resourceType;
int16 _animationNr;
bool _mirroredFlag;
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 1208f8ed65..8f4fe094a8 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "common/archive.h"
#include "common/util.h"
#include "common/stack.h"
#include "graphics/primitives.h"
@@ -30,7 +31,6 @@
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/engine/state.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/portrait.h"
@@ -38,8 +38,8 @@
namespace Sci {
-Portrait::Portrait(ResourceManager *resMan, SciEvent *event, SciGui *gui, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName)
- : _resMan(resMan), _event(event), _gui(gui), _screen(screen), _palette(palette), _audio(audio), _resourceName(resourceName) {
+Portrait::Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName)
+ : _resMan(resMan), _event(event), _screen(screen), _palette(palette), _audio(audio), _resourceName(resourceName) {
init();
}
@@ -166,7 +166,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
// Do animation depending on sync resource till audio is done playing
uint16 syncCue;
int timerPosition, curPosition;
- sciEvent curEvent;
+ SciEvent curEvent;
bool userAbort = false;
while ((syncOffset < syncResource->size - 2) && (!userAbort)) {
@@ -181,8 +181,8 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
// Wait till syncTime passed, then show specific animation bitmap
do {
- _gui->wait(1);
- curEvent = _event->get(SCI_EVENT_ANY);
+ g_sci->getEngineState()->wait(1);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
if (curEvent.type == SCI_EVENT_MOUSE_PRESS ||
(curEvent.type == SCI_EVENT_KEYBOARD && curEvent.data == SCI_KEY_ESC) ||
g_engine->shouldQuit())
diff --git a/engines/sci/graphics/portrait.h b/engines/sci/graphics/portrait.h
index 4b22e209a3..7da9425c9d 100644
--- a/engines/sci/graphics/portrait.h
+++ b/engines/sci/graphics/portrait.h
@@ -42,7 +42,7 @@ struct PortraitBitmap {
*/
class Portrait {
public:
- Portrait(ResourceManager *resMan, SciEvent *event, SciGui *gui, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName);
+ Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName);
~Portrait();
void setupAudio(uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
@@ -56,8 +56,7 @@ private:
void bitsShow();
ResourceManager *_resMan;
- SciEvent *_event;
- SciGui *_gui;
+ EventManager *_event;
GfxPalette *_palette;
GfxScreen *_screen;
AudioPlayer *_audio;
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index ab3291dd79..0a0e49235e 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -27,7 +27,9 @@
#include "sci/sci.h"
#include "sci/engine/features.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
+#include "sci/engine/selector.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint16.h"
#include "sci/graphics/animate.h"
@@ -50,18 +52,22 @@ GfxPorts::GfxPorts(SegManager *segMan, GfxScreen *screen)
}
GfxPorts::~GfxPorts() {
- // TODO: Clear _windowList and delete all stuff in it?
+ // reset frees all windows but _picWind
+ reset();
+ freeWindow(_picWind);
+ delete _wmgrPort;
delete _menuPort;
}
-void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16, Common::String gameId) {
+void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *text16) {
int16 offTop = 10;
_usesOldGfxFunctions = usesOldGfxFunctions;
- _gui = gui;
_paint16 = paint16;
_text16 = text16;
+ _freeCounter = 0;
+
// _menuPort has actually hardcoded id 0xFFFF. Its not meant to be known to windowmanager according to sierra sci
_menuPort = new Port(0xFFFF);
openPort(_menuPort);
@@ -85,37 +91,103 @@ void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16,
else
_styleUser = SCI_WINDOWMGR_STYLE_USER | SCI_WINDOWMGR_STYLE_TRANSPARENT;
- // Jones, Slater and Hoyle 3 were called with parameter -Nw 0 0 200 320.
- // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use SetPort so we don't need to set the other fields.
+ // Jones, Slater, Hoyle 3&4 and Crazy Nicks Laura Bow/Kings Quest were
+ // called with parameter -Nw 0 0 200 320.
+ // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use
+ // SetPort so we don't need to set the other fields.
// This actually meant not skipping the first 10 pixellines in windowMgrPort
- if (gameId == "jones" || gameId == "slater" || gameId == "hoyle3" || (gameId == "mothergoose" && getSciVersion() == SCI_VERSION_1_EARLY))
+ switch (g_sci->getGameId()) {
+ case GID_JONES:
+ case GID_SLATER:
+ case GID_HOYLE3:
+ case GID_HOYLE4:
+ case GID_CNICK_LAURABOW:
+ case GID_CNICK_KQ:
offTop = 0;
+ break;
+ case GID_MOTHERGOOSE:
+ // TODO: if mother goose EGA also uses offTop we can simply remove this check altogether
+ switch (getSciVersion()) {
+ case SCI_VERSION_1_EARLY:
+ case SCI_VERSION_1_1:
+ offTop = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ case GID_FAIRYTALES:
+ // Mixed-Up Fairy Tales (& its demo) uses -w 26 0 200 320. If we don't
+ // also do this we will get not-fully-removed windows everywhere.
+ offTop = 26;
+ break;
+ default:
+ offTop = 10;
+ break;
+ }
openPort(_wmgrPort);
setPort(_wmgrPort);
// SCI0 games till kq4 (.502 - not including) did not adjust against _wmgrPort in kNewWindow
// We leave _wmgrPort top at 0, so the adjustment wont get done
- if (!g_sci->_features->usesOldGfxFunctions())
+ if (!g_sci->_features->usesOldGfxFunctions()) {
setOrigin(0, offTop);
- _wmgrPort->rect.bottom = _screen->getHeight() - offTop;
+ _wmgrPort->rect.bottom = _screen->getHeight() - offTop;
+ } else {
+ _wmgrPort->rect.bottom = _screen->getHeight();
+ }
_wmgrPort->rect.right = _screen->getWidth();
_wmgrPort->rect.moveTo(0, 0);
_wmgrPort->curTop = 0;
_wmgrPort->curLeft = 0;
_windowList.push_front(_wmgrPort);
- _picWind = newWindow(Common::Rect(0, offTop, _screen->getWidth(), _screen->getHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true);
+ _picWind = addWindow(Common::Rect(0, offTop, _screen->getWidth(), _screen->getHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true);
// For SCI0 games till kq4 (.502 - not including) we set _picWind top to offTop instead
// Because of the menu/status bar
if (g_sci->_features->usesOldGfxFunctions())
_picWind->top = offTop;
- priorityBandsMemoryActive = false;
-
kernelInitPriorityBands();
}
+// Removes any windows from windowList
+// is used when restoring/restarting the game
+// Sierra SCI actually saved the whole windowList, it seems we don't need to do this at all
+// but in some games there are still windows active when restoring. Leaving those windows open
+// would create all sorts of issues, that's why we remove them
+void GfxPorts::reset() {
+ setPort(_picWind);
+
+ // free everything after _picWind
+ for (uint id = PORTS_FIRSTSCRIPTWINDOWID; id < _windowsById.size(); id++) {
+ Window *window = (Window *)_windowsById[id];
+ if (window)
+ freeWindow(window);
+ }
+ _freeCounter = 0;
+ _windowList.clear();
+ _windowList.push_front(_wmgrPort);
+ _windowList.push_back(_picWind);
+}
+
void GfxPorts::kernelSetActive(uint16 portId) {
+ if (_freeCounter) {
+ // Windows waiting to get freed
+ for (uint id = PORTS_FIRSTSCRIPTWINDOWID; id < _windowsById.size(); id++) {
+ Window *window = (Window *)_windowsById[id];
+ if (window) {
+ if (window->counterTillFree) {
+ window->counterTillFree--;
+ if (!window->counterTillFree) {
+ freeWindow(window);
+ _freeCounter--;
+ }
+ }
+ }
+ }
+ }
+
switch (portId) {
case 0:
setPort(_wmgrPort);
@@ -123,8 +195,13 @@ void GfxPorts::kernelSetActive(uint16 portId) {
case 0xFFFF:
setPort(_menuPort);
break;
- default:
- setPort(getPortById(portId));
+ default: {
+ Port *newPort = getPortById(portId);
+ if (newPort)
+ setPort(newPort);
+ else
+ error("GfxPorts::kernelSetActive was requested to set invalid port id %d", portId);
+ }
};
}
@@ -149,10 +226,10 @@ reg_t GfxPorts::kernelGetActive() {
reg_t GfxPorts::kernelNewWindow(Common::Rect dims, Common::Rect restoreRect, uint16 style, int16 priority, int16 colorPen, int16 colorBack, const char *title) {
Window *wnd = NULL;
- if (restoreRect.top != 0 && restoreRect.left != 0 && restoreRect.height() != 0 && restoreRect.width() != 0)
- wnd = newWindow(dims, &restoreRect, title, style, priority, false);
+ if (restoreRect.bottom != 0 && restoreRect.right != 0)
+ wnd = addWindow(dims, &restoreRect, title, style, priority, false);
else
- wnd = newWindow(dims, NULL, title, style, priority, false);
+ wnd = addWindow(dims, NULL, title, style, priority, false);
wnd->penClr = colorPen;
wnd->backClr = colorBack;
drawWindow(wnd);
@@ -162,7 +239,15 @@ reg_t GfxPorts::kernelNewWindow(Common::Rect dims, Common::Rect restoreRect, uin
void GfxPorts::kernelDisposeWindow(uint16 windowId, bool reanimate) {
Window *wnd = (Window *)getPortById(windowId);
- disposeWindow(wnd, reanimate);
+ if (wnd) {
+ if (!wnd->counterTillFree) {
+ removeWindow(wnd, reanimate);
+ } else {
+ error("kDisposeWindow: used already disposed window id %d", windowId);
+ }
+ } else {
+ error("kDisposeWindow: used unknown window id %d", windowId);
+ }
}
int16 GfxPorts::isFrontWindow(Window *pWnd) {
@@ -199,9 +284,9 @@ void GfxPorts::endUpdate(Window *wnd) {
setPort(oldPort);
}
-Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restoreRect, const char *title, uint16 style, int16 priority, bool draw) {
+Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restoreRect, const char *title, uint16 style, int16 priority, bool draw) {
// Find an unused window/port id
- uint id = 1;
+ uint id = PORTS_FIRSTWINDOWID;
while (id < _windowsById.size() && _windowsById[id]) {
++id;
}
@@ -213,7 +298,7 @@ Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restor
Common::Rect r;
if (!pwnd) {
- warning("Can't open window!");
+ error("Can't open window!");
return 0;
}
@@ -226,8 +311,10 @@ Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restor
r = dims;
if (r.width() > _screen->getWidth()) {
- // We get invalid dimensions at least at the end of sq3 (script bug!)
- warning("fixing too large window, given left&right was %d, %d", dims.left, dims.right);
+ // We get invalid dimensions at least at the end of sq3 (script bug!).
+ // Same happens very often in lsl5, sierra sci didnt fix it but it looked awful.
+ // Also happens frequently in the demo of GK1.
+ warning("Fixing too large window, left: %d, right: %d", dims.left, dims.right);
r.left = 0;
r.right = _screen->getWidth() - 1;
if ((style != _styleUser) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME))
@@ -342,22 +429,40 @@ void GfxPorts::drawWindow(Window *pWnd) {
if (!(wndStyle & SCI_WINDOWMGR_STYLE_TRANSPARENT))
_paint16->fillRect(r, GFX_SCREEN_MASK_VISUAL, pWnd->backClr);
- _paint16->bitsShow(pWnd->restoreRect);
+ _paint16->bitsShow(pWnd->dims);
}
setPort(oldport);
}
-void GfxPorts::disposeWindow(Window *pWnd, bool reanimate) {
+void GfxPorts::removeWindow(Window *pWnd, bool reanimate) {
setPort(_wmgrPort);
_paint16->bitsRestore(pWnd->hSaved1);
+ pWnd->hSaved1 = NULL_REG;
_paint16->bitsRestore(pWnd->hSaved2);
+ pWnd->hSaved2 = NULL_REG;
if (!reanimate)
_paint16->bitsShow(pWnd->restoreRect);
else
_paint16->kernelGraphRedrawBox(pWnd->restoreRect);
_windowList.remove(pWnd);
setPort(_windowList.back());
- _windowsById[pWnd->id] = 0;
+ // We will actually free this window after 10 kSetPort-calls
+ // Sierra sci freed the pointer immediately, but pointer to that port
+ // still worked till the memory got overwritten. Some games depend
+ // on this (dispose a window and then kSetPort to it again for once)
+ // Those are actually script bugs, but patching all of those out
+ // would be quite a hassle and this just keeps compatibility
+ // (examples: hoyle 4 game menu and sq4cd inventory)
+ pWnd->counterTillFree = 10;
+ _freeCounter++;
+}
+
+void GfxPorts::freeWindow(Window *pWnd) {
+ if (!pWnd->hSaved1.isNull())
+ _segMan->freeHunkEntry(pWnd->hSaved1);
+ if (!pWnd->hSaved2.isNull())
+ _segMan->freeHunkEntry(pWnd->hSaved1);
+ _windowsById[pWnd->id] = NULL;
delete pWnd;
}
@@ -377,13 +482,9 @@ void GfxPorts::updateWindow(Window *wnd) {
}
Port *GfxPorts::getPortById(uint16 id) {
- if (id > _windowsById.size())
- error("getPortById() received invalid id");
- return _windowsById[id];
+ return (id < _windowsById.size()) ? _windowsById[id] : NULL;
}
-
-
Port *GfxPorts::setPort(Port *newPort) {
Port *oldPort = _curPort;
_curPort = newPort;
@@ -495,6 +596,11 @@ void GfxPorts::priorityBandsInit(int16 bandCount, int16 top, int16 bottom) {
// We fill space that is left over with the highest band (hardcoded 200 limit, because this algo isnt meant to be used on hires)
for (y = _priorityBottom; y < 200; y++)
_priorityBands[y] = _priorityBandCount;
+
+ // adjust, if bottom is 200 (one over the actual screen range) - we could otherwise go possible out of bounds
+ // sierra sci also adjust accordingly
+ if (_priorityBottom == 200)
+ _priorityBottom--;
}
void GfxPorts::priorityBandsInit(byte *data) {
@@ -510,22 +616,14 @@ void GfxPorts::priorityBandsInit(byte *data) {
_priorityBands[i++] = inx;
}
-// Gets used by picture class to remember priority bands data from sci1.1 pictures that need to get applied when
-// transitioning to that picture
-void GfxPorts::priorityBandsRemember(byte *data) {
- int bandNo;
- for (bandNo = 0; bandNo < 14; bandNo++) {
- priorityBandsMemory[bandNo] = READ_LE_UINT16(data);
+// Gets used to read priority bands data from sci1.1 pictures
+void GfxPorts::priorityBandsInitSci11(byte *data) {
+ byte priorityBands[14];
+ for (int bandNo = 0; bandNo < 14; bandNo++) {
+ priorityBands[bandNo] = READ_LE_UINT16(data);
data += 2;
}
- priorityBandsMemoryActive = true;
-}
-
-void GfxPorts::priorityBandsRecall() {
- if (priorityBandsMemoryActive) {
- priorityBandsInit((byte *)&priorityBandsMemory);
- priorityBandsMemoryActive = false;
- }
+ priorityBandsInit(priorityBands);
}
void GfxPorts::kernelInitPriorityBands() {
diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h
index 0876d9e442..42a0a5d4b4 100644
--- a/engines/sci/graphics/ports.h
+++ b/engines/sci/graphics/ports.h
@@ -36,6 +36,9 @@ class GfxPaint16;
class GfxScreen;
class GfxText16;
+#define PORTS_FIRSTWINDOWID 2
+#define PORTS_FIRSTSCRIPTWINDOWID 3
+
/**
* Ports class, includes all port managment for SCI0->SCI1.1 games. Ports are some sort of windows in SCI
* this class also handles adjusting coordinates to a specific port
@@ -45,7 +48,8 @@ public:
GfxPorts(SegManager *segMan, GfxScreen *screen);
~GfxPorts();
- void init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16, Common::String gameId);
+ void init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *text16);
+ void reset();
void kernelSetActive(uint16 portId);
Common::Rect kernelGetPicWindow(int16 &picTop, int16 &picLeft);
@@ -57,9 +61,10 @@ public:
int16 isFrontWindow(Window *wnd);
void beginUpdate(Window *wnd);
void endUpdate(Window *wnd);
- Window *newWindow(const Common::Rect &dims, const Common::Rect *restoreRect, const char *title, uint16 style, int16 priority, bool draw);
+ Window *addWindow(const Common::Rect &dims, const Common::Rect *restoreRect, const char *title, uint16 style, int16 priority, bool draw);
void drawWindow(Window *wnd);
- void disposeWindow(Window *pWnd, bool reanimate);
+ void removeWindow(Window *pWnd, bool reanimate);
+ void freeWindow(Window *pWnd);
void updateWindow(Window *wnd);
Port *getPortById(uint16 id);
@@ -81,8 +86,7 @@ public:
void priorityBandsInit(int16 bandCount, int16 top, int16 bottom);
void priorityBandsInit(byte *data);
- void priorityBandsRemember(byte *data);
- void priorityBandsRecall();
+ void priorityBandsInitSci11(byte *data);
void kernelInitPriorityBands();
void kernelGraphAdjustPriority(int top, int bottom);
@@ -102,7 +106,6 @@ private:
typedef Common::List<Port *> PortList;
SegManager *_segMan;
- SciGui *_gui;
GfxPaint16 *_paint16;
GfxScreen *_screen;
GfxText16 *_text16;
@@ -111,6 +114,9 @@ private:
uint16 _styleUser;
+ // counts windows that got disposed but are not freed yet
+ uint16 _freeCounter;
+
/** The list of open 'windows' (and ports), in visual order. */
PortList _windowList;
@@ -122,9 +128,6 @@ private:
// Priority Bands related variables
int16 _priorityTop, _priorityBottom, _priorityBandCount;
byte _priorityBands[200];
-
- byte priorityBandsMemory[14];
- bool priorityBandsMemoryActive;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/robot.cpp b/engines/sci/graphics/robot.cpp
index 2f711eb58a..1572a0a9ec 100644
--- a/engines/sci/graphics/robot.cpp
+++ b/engines/sci/graphics/robot.cpp
@@ -28,25 +28,42 @@
#include "sci/graphics/screen.h"
#include "sci/graphics/robot.h"
+#include "common/file.h"
+
namespace Sci {
#ifdef ENABLE_SCI32
-Robot::Robot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId)
+GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId)
: _resMan(resMan), _screen(screen), _resourceId(resourceId) {
assert(resourceId != -1);
initData(resourceId);
+ _resourceData = 0;
}
-Robot::~Robot() {
- _resMan->unlockResource(_resource);
+GfxRobot::~GfxRobot() {
+ delete[] _resourceData;
}
-void Robot::initData(GuiResourceId resourceId) {
- _resource = _resMan->findResource(ResourceId(kResourceTypeRobot, resourceId), true);
- if (!_resource) {
- error("robot resource %d not found", resourceId);
+void GfxRobot::initData(GuiResourceId resourceId) {
+ char fileName[10];
+ sprintf(fileName, "%d.rbt", resourceId);
+
+ Common::File robotFile;
+ if (robotFile.open(fileName)) {
+ _resourceData = new byte[robotFile.size()];
+ robotFile.read(_resourceData, robotFile.size());
+ robotFile.close();
+ } else {
+ warning("Unable to open robot file %s", fileName);
+ return;
+ }
+
+ byte version = _resourceData[6];
+
+ if (version != 4 && version != 5) {
+ warning("Robot version %d isn't supported yet", version);
+ return;
}
- _resourceData = _resource->data;
// sample data:
// Header - 14 bytes
@@ -152,13 +169,16 @@ void Robot::initData(GuiResourceId resourceId) {
// ^ ??
// 00000120: 70 70 70 70 70 70 70 70-70 70 70 70 70 70 70 70 pppppppppppppppp
- _frameCount = READ_LE_UINT16(_resourceData + 12);
- _frameSize = READ_LE_UINT32(_resourceData + 34);
+ _frameCount = READ_LE_UINT16(_resourceData + 14);
+ //_frameSize = READ_LE_UINT32(_resourceData + 34);
+ byte hasSound = _resourceData[25];
+
+ debug("Robot %d, %d frames, sound: %d\n", resourceId, _frameCount, hasSound);
}
// TODO: just trying around in here...
-void Robot::draw() {
+void GfxRobot::draw() {
byte *bitmapData = _resourceData + ROBOT_FILE_STARTOFDATA;
int x, y;
//int frame;
diff --git a/engines/sci/graphics/robot.h b/engines/sci/graphics/robot.h
index 009b76a91c..3ea9a7f735 100644
--- a/engines/sci/graphics/robot.h
+++ b/engines/sci/graphics/robot.h
@@ -31,10 +31,10 @@ namespace Sci {
#define ROBOT_FILE_STARTOFDATA 58
#ifdef ENABLE_SCI32
-class Robot {
+class GfxRobot {
public:
- Robot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId);
- ~Robot();
+ GfxRobot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId);
+ ~GfxRobot();
void draw();
@@ -45,7 +45,6 @@ private:
GfxScreen *_screen;
GuiResourceId _resourceId;
- Resource *_resource;
byte *_resourceData;
uint16 _width;
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 7ca9e33509..839b9975c5 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -35,8 +35,35 @@
namespace Sci {
-GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int upscaledHires) :
- _resMan(resMan), _width(width), _height(height), _upscaledHires(upscaledHires) {
+GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
+
+ // Scale the screen, if needed
+ _upscaledHires = GFX_SCREEN_UPSCALED_DISABLED;
+
+ // King's Quest 6 and Gabriel Knight 1 have hires content, gk1/cd was able
+ // to provide that under DOS as well, but as gk1/floppy does support
+ // upscaled hires scriptswise, but doesn't actually have the hires content
+ // we need to limit it to platform windows.
+ if (g_sci->getPlatform() == Common::kPlatformWindows) {
+ if (g_sci->getGameId() == GID_KQ6)
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x440;
+#ifdef ENABLE_SCI32
+ if (g_sci->getGameId() == GID_GK1)
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
+#endif
+ }
+
+ if (_resMan->detectHires()) {
+ _width = 640;
+ _height = 480;
+ } else {
+ _width = 320;
+ _height = 200;
+ }
+
+ // Japanese versions of games use hi-res font on upscaled version of the game.
+ if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x400;
_pixels = _width * _height;
@@ -80,8 +107,9 @@ GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int ups
_unditherState = true;
if (_resMan->isVGA() || (_resMan->isAmiga32color())) {
- // It's not 100% accurate to set white to be 255 for amiga 32-color games
- // 255 is defined as white in our sci at all times, so it doesnt matter
+ // It is not 100% accurate to set white to be 255 for Amiga 32-color
+ // games. But 255 is defined as white in our SCI at all times, so it
+ // doesn't matter.
_colorWhite = 255;
if (getSciVersion() >= SCI_VERSION_1_1)
_colorDefaultVectorData = 255;
@@ -93,7 +121,18 @@ GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int ups
}
// Initialize the actual screen
- initGraphics(_displayWidth, _displayHeight, _displayWidth > 320);
+
+ if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) {
+ // For SCI1.1 Mac, we need to expand the screen to accommodate for
+ // the icon bar. Of course, both KQ6 and QFG1 VGA differ in size.
+ if (g_sci->getGameId() == GID_KQ6)
+ initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320);
+ else if (g_sci->getGameId() == GID_QFG1VGA)
+ initGraphics(_displayWidth, _displayHeight + 20, _displayWidth > 320);
+ else
+ error("Unknown SCI1.1 Mac game");
+ } else
+ initGraphics(_displayWidth, _displayHeight, _displayWidth > 320);
}
GfxScreen::~GfxScreen() {
@@ -130,7 +169,10 @@ void GfxScreen::copyRectToScreen(const Common::Rect &rect) {
}
}
-// This copies a rect to screen w/o scaling adjustment and is only meant to be used on hires graphics used in upscaled hires mode
+/**
+ * This copies a rect to screen w/o scaling adjustment and is only meant to be
+ * used on hires graphics used in upscaled hires mode.
+ */
void GfxScreen::copyDisplayRectToScreen(const Common::Rect &rect) {
if (!_upscaledHires)
error("copyDisplayRectToScreen: not in upscaled hires mode");
@@ -181,15 +223,42 @@ void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority,
_controlScreen[offset] = control;
}
-// This will just change a pixel directly on displayscreen. Its supposed to get only used on upscaled-Hires games where
-// hires content needs to get drawn ONTO the upscaled display screen (like japanese fonts, hires portraits, etc.)
+/**
+ * This is used to put font pixels onto the screen - we adjust differently, so that we won't
+ * do triple pixel lines in any case on upscaled hires. That way the font will not get distorted
+ * Sierra SCI didn't do this
+ */
+void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) {
+ int offset = (startingY + y) * _width + x;
+
+ _visualScreen[offset] = color;
+ if (!_upscaledHires) {
+ _displayScreen[offset] = color;
+ } else {
+ int displayOffset = (_upscaledMapping[startingY] + y * 2) * _displayWidth + x * 2;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ displayOffset += _displayWidth;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ }
+}
+
+/**
+ * This will just change a pixel directly on displayscreen. It is supposed to be
+ * only used on upscaled-Hires games where hires content needs to get drawn ONTO
+ * the upscaled display screen (like japanese fonts, hires portraits, etc.).
+ */
void GfxScreen::putPixelOnDisplay(int x, int y, byte color) {
int offset = y * _displayWidth + x;
_displayScreen[offset] = color;
}
-// Sierra's Bresenham line drawing
-// WARNING: Do not just blindly replace this with Graphics::drawLine(), as it seems to create issues with flood fill
+/**
+ * Sierra's Bresenham line drawing.
+ * WARNING: Do not replace this with Graphics::drawLine(), as this causes issues
+ * with flood fill, due to small difference in the Bresenham logic.
+ */
void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
int16 left = startPoint.x;
int16 top = startPoint.y;
@@ -252,7 +321,8 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte
}
}
-// We put hires kanji chars onto upscaled background, so we need to adjust coordinates. Caller gives use low-res ones
+// We put hires kanji chars onto upscaled background, so we need to adjust
+// coordinates. Caller gives use low-res ones.
void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color) {
byte *displayPtr = _displayScreen + y * _displayWidth * 2 + x * 2;
// we don't use outline, so color 0 is actually not used
@@ -389,6 +459,11 @@ void GfxScreen::bitsRestore(byte *memoryPtr) {
if (!_upscaledHires)
error("bitsRestore() called w/o being in upscaled hires mode");
bitsRestoreScreen(rect, memoryPtr, _displayScreen, _displayWidth);
+ // WORKAROUND - we are not sure what sierra is doing. If we don't do this here, portraits won't get fully removed
+ // from screen. Some lowres showBits() call is used for that and it's not covering the whole area
+ // We would need to find out inside the kq6 windows interpreter, but this here works already and seems not to have
+ // any side-effects. The whole hires is hacked into the interpreter, so maybe this is even right.
+ copyDisplayRectToScreen(rect);
}
}
@@ -424,7 +499,19 @@ void GfxScreen::bitsRestoreDisplayScreen(Common::Rect rect, byte *&memoryPtr) {
}
}
-void GfxScreen::setPalette(Palette*pal) {
+void GfxScreen::getPalette(Palette *pal) {
+ // just copy palette to system
+ byte bpal[4 * 256];
+ // Get current palette, update it and put back
+ g_system->grabPalette(bpal, 0, 256);
+ for (int16 i = 1; i < 255; i++) {
+ pal->colors[i].r = bpal[i * 4];
+ pal->colors[i].g = bpal[i * 4 + 1];
+ pal->colors[i].b = bpal[i * 4 + 2];
+ }
+}
+
+void GfxScreen::setPalette(Palette *pal) {
// just copy palette to system
byte bpal[4 * 256];
// Get current palette, update it and put back
@@ -447,6 +534,22 @@ void GfxScreen::setVerticalShakePos(uint16 shakePos) {
g_system->setShakePos(shakePos * 2);
}
+void GfxScreen::kernelShakeScreen(uint16 shakeCount, uint16 directions) {
+ while (shakeCount--) {
+ if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
+ setVerticalShakePos(10);
+ // TODO: horizontal shakes
+ g_system->updateScreen();
+ g_sci->getEngineState()->wait(3);
+
+ if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
+ setVerticalShakePos(0);
+
+ g_system->updateScreen();
+ g_sci->getEngineState()->wait(3);
+ }
+}
+
void GfxScreen::dither(bool addToFlag) {
int y, x;
byte color, ditheredColor;
@@ -546,19 +649,20 @@ void GfxScreen::debugShowMap(int mapNo) {
copyToScreen();
}
-void GfxScreen::scale2x(byte *src, byte *dst, int16 srcWidth, int16 srcHeight) {
- int newWidth = srcWidth * 2;
- byte *srcPtr = src;
+void GfxScreen::scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight) {
+ const int newWidth = srcWidth * 2;
+ const byte *srcPtr = src;
for (int y = 0; y < srcHeight; y++) {
for (int x = 0; x < srcWidth; x++) {
- int destOffset = y * 2 * newWidth + x * 2;
- dst[destOffset] = *srcPtr;
- dst[destOffset + 1] = *srcPtr;
- dst[destOffset + newWidth] = *srcPtr;
- dst[destOffset + newWidth + 1] = *srcPtr;
- srcPtr++;
+ const byte color = *srcPtr++;
+ dst[0] = color;
+ dst[1] = color;
+ dst[newWidth] = color;
+ dst[newWidth + 1] = color;
+ dst += 2;
}
+ dst += newWidth;
}
}
@@ -567,6 +671,24 @@ void GfxScreen::adjustToUpscaledCoordinates(int16 &y, int16 &x) {
y = _upscaledMapping[y];
}
+void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x) {
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_640x400:
+ x /= 2;
+ y /= 2;
+ break;
+ case GFX_SCREEN_UPSCALED_640x440:
+ x /= 2;
+ y = (y * 5) / 11;
+ break;
+ case GFX_SCREEN_UPSCALED_640x480:
+ x /= 2;
+ y = (y * 5) / 12;
+ default:
+ break;
+ }
+}
+
int16 GfxScreen::kernelPicNotValid(int16 newPicNotValid) {
int16 oldPicNotValid;
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index b2479e9735..97f5736289 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -50,16 +50,21 @@ enum GfxScreenMasks {
GFX_SCREEN_MASK_ALL = GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY|GFX_SCREEN_MASK_CONTROL
};
-#define SCI_SCREEN_UNDITHERMEMORIAL_SIZE 256
+enum {
+ SCI_SCREEN_UNDITHERMEMORIAL_SIZE = 256
+};
/**
- * Screen class, actually creates 3 (4) screens internally - which is visual/display (for the user),
- * priority (contains priority information) and control (contains control information). Handles all operations to it
- * and copies parts of visual/display screen to the actual screen, so the user can really see it.
+ * Screen class, actually creates 3 (4) screens internally:
+ * - visual/display (for the user),
+ * - priority (contains priority information) and
+ * - control (contains control information).
+ * Handles all operations to it and copies parts of visual/display screen to
+ * the actual screen, so the user can really see it.
*/
class GfxScreen {
public:
- GfxScreen(ResourceManager *resMan, int16 width = 320, int16 height = 200, int upscaledHires = GFX_SCREEN_UPSCALED_DISABLED);
+ GfxScreen(ResourceManager *resMan);
~GfxScreen();
uint16 getWidth() { return _width; }
@@ -78,15 +83,16 @@ public:
byte getDrawingMask(byte color, byte prio, byte control);
void putPixel(int x, int y, byte drawMask, byte color, byte prio, byte control);
+ void putFontPixel(int startingY, int x, int y, byte color);
void putPixelOnDisplay(int x, int y, byte color);
void drawLine(Common::Point startPoint, Common::Point endPoint, byte color, byte prio, byte control);
void drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte prio, byte control) {
drawLine(Common::Point(left, top), Common::Point(right, bottom), color, prio, control);
}
- int getUpscaledHires() {
+ int getUpscaledHires() const {
return _upscaledHires;
}
- bool getUnditherState() {
+ bool getUnditherState() const {
return _unditherState;
}
void putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color);
@@ -100,13 +106,13 @@ public:
void bitsGetRect(byte *memoryPtr, Common::Rect *destRect);
void bitsRestore(byte *memoryPtr);
- void setPalette(Palette*pal);
-
- void setVerticalShakePos(uint16 shakePos);
+ void getPalette(Palette *pal);
+ void setPalette(Palette *pal);
- void scale2x(byte *src, byte *dst, int16 srcWidth, int16 srcHeight);
+ void scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight);
void adjustToUpscaledCoordinates(int16 &y, int16 &x);
+ void adjustBackUpscaledCoordinates(int16 &y, int16 &x);
void dither(bool addToFlag);
void debugUnditherSetState(bool flag);
@@ -118,6 +124,7 @@ public:
int _picNotValidSci11; // another variable that is used by kPicNotValid in sci1.1
int16 kernelPicNotValid(int16 newPicNotValid);
+ void kernelShakeScreen(uint16 shakeCount, uint16 direction);
private:
uint16 _width;
@@ -135,30 +142,39 @@ private:
void bitsSaveScreen(Common::Rect rect, byte *screen, uint16 screenWidth, byte *&memoryPtr);
void bitsSaveDisplayScreen(Common::Rect rect, byte *&memoryPtr);
+ void setVerticalShakePos(uint16 shakePos);
+
bool _unditherState;
int16 _unditherMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
- // these screens have the real resolution of the game engine (320x200 for SCI0/SCI1/SCI11 games, 640x480 for SCI2 games)
- // SCI0 games will be dithered in here at any time
+ // These screens have the real resolution of the game engine (320x200 for
+ // SCI0/SCI1/SCI11 games, 640x480 for SCI2 games). SCI0 games will be
+ // dithered in here at any time.
byte *_visualScreen;
byte *_priorityScreen;
byte *_controlScreen;
- // this screen is the one that is actually displayed to the user. It may be 640x400 for japanese SCI1 games
- // SCI0 games may be undithered in here. Only read from this buffer for Save/ShowBits usage.
+ // This screen is the one that is actually displayed to the user. It may be
+ // 640x400 for japanese SCI1 games. SCI0 games may be undithered in here.
+ // Only read from this buffer for Save/ShowBits usage.
byte *_displayScreen;
Common::Rect getScaledRect(Common::Rect rect);
ResourceManager *_resMan;
- // this is a pointer to the currently active screen (changing it only required for debug purposes)
+ /**
+ * Pointer to the currently active screen (changing it only required for
+ * debug purposes).
+ */
byte *_activeScreen;
- // this variable defines, if upscaled hires is active and what upscaled mode is used
+ // This variable defines, if upscaled hires is active and what upscaled mode
+ // is used.
int _upscaledHires;
- // this here holds a translation for vertical coordinates between native (visual) and actual (display) screen
+ // This here holds a translation for vertical coordinates between native
+ // (visual) and actual (display) screen.
int _upscaledMapping[SCI_SCREEN_UPSCALEDMAXHEIGHT + 1];
};
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index 952d13fbbd..fc07febe14 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -73,28 +73,6 @@ void GfxText16::SetFont(GuiResourceId fontId) {
_ports->_curPort->fontHeight = _font->getHeight();
}
-void GfxText16::CodeSetFonts(int argc, reg_t *argv) {
- int i;
-
- delete _codeFonts;
- _codeFontsCount = argc;
- _codeFonts = new GuiResourceId[argc];
- for (i = 0; i < argc; i++) {
- _codeFonts[i] = (GuiResourceId)argv[i].toUint16();
- }
-}
-
-void GfxText16::CodeSetColors(int argc, reg_t *argv) {
- int i;
-
- delete _codeColors;
- _codeColorsCount = argc;
- _codeColors = new uint16[argc];
- for (i = 0; i < argc; i++) {
- _codeColors[i] = argv[i].toUint16();
- }
-}
-
void GfxText16::ClearChar(int16 chr) {
if (_ports->_curPort->penMode != 1)
return;
@@ -106,10 +84,10 @@ void GfxText16::ClearChar(int16 chr) {
_paint16->eraseRect(rect);
}
-// This internal function gets called as soon as a '|' is found in a text
-// It will process the encountered code and set new font/set color
-// We only support one-digit codes currently, don't know if multi-digit codes are possible
-// Returns textcode character count
+// This internal function gets called as soon as a '|' is found in a text. It
+// will process the encountered code and set new font/set color. We only support
+// one-digit codes currently, don't know if multi-digit codes are possible.
+// Returns textcode character count.
int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor) {
const char *textCode = text;
int16 textCodeSize = 0;
@@ -155,15 +133,17 @@ int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int1
static const uint16 text16_punctuationSjis[] = {
0x9F82, 0xA182, 0xA382, 0xA582, 0xA782, 0xC182, 0xA782, 0xC182, 0xE182, 0xE382, 0xE582, 0xEC82,
0x4083, 0x4283, 0x4483, 0x4683, 0x4883, 0x6283, 0x8383, 0x8583, 0x8783, 0x8E83, 0x9583, 0x9683,
- 0x5B81, 0x4181, 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0 };
+ 0x5B81, 0x4181, 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0
+};
-// return max # of chars to fit maxwidth with full words, does not include breaking space
+// return max # of chars to fit maxwidth with full words, does not include
+// breaking space
int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId) {
uint16 curChar = 0;
int16 maxChars = 0, curCharCount = 0;
uint16 width = 0;
- GuiResourceId oldFontId = GetFontId();
- int16 oldPenColor = _ports->_curPort->penClr;
+ GuiResourceId previousFontId = GetFontId();
+ int16 previousPenColor = _ports->_curPort->penClr;
GetFont();
if (!_font)
@@ -179,7 +159,7 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
case 0x7C:
if (getSciVersion() >= SCI_VERSION_1_1) {
curCharCount++;
- curCharCount += CodeProcessing(text, orgFontId, oldPenColor);
+ curCharCount += CodeProcessing(text, orgFontId, previousPenColor);
continue;
}
break;
@@ -200,8 +180,8 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
curCharCount++;
// and it's also meant to pass through here
case 0:
- SetFont(oldFontId);
- _ports->penColor(oldPenColor);
+ SetFont(previousFontId);
+ _ports->penColor(previousPenColor);
return curCharCount;
case ' ':
@@ -217,9 +197,10 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
uint16 nextChar;
- // we remove the last char only, if maxWidth was actually equal width before adding the last char
- // otherwise we won't get the same cutting as in sierra pc98 sci
- // note: changing the while() instead will NOT WORK. it would break all sorts of regular sci games
+ // We remove the last char only, if maxWidth was actually equal width
+ // before adding the last char. Otherwise we won't get the same cutting
+ // as in sierra pc98 sci. Note: changing the while() instead will NOT
+ // WORK. it would break all sorts of regular sci games.
if (maxWidth == (width - _font->getCharWidth(curChar))) {
maxChars--;
if (curChar > 0xFF)
@@ -245,15 +226,15 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
}
}
}
- SetFont(oldFontId);
- _ports->penColor(oldPenColor);
+ SetFont(previousFontId);
+ _ports->penColor(previousPenColor);
return maxChars;
}
-void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight) {
+void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont) {
uint16 curChar;
- GuiResourceId oldFontId = GetFontId();
- int16 oldPenColor = _ports->_curPort->penClr;
+ GuiResourceId previousFontId = GetFontId();
+ int16 previousPenColor = _ports->_curPort->penClr;
textWidth = 0; textHeight = 0;
@@ -283,13 +264,18 @@ void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId org
}
}
}
- SetFont(oldFontId);
- _ports->penColor(oldPenColor);
+ // When calculating size, we do not restore font because we need the current (code modified) font active
+ // If we are drawing this is called inbetween, so font needs to get restored
+ // If we are calculating size of just one fixed string (::StringWidth), then we need to restore
+ if (restoreFont) {
+ SetFont(previousFontId);
+ _ports->penColor(previousPenColor);
+ }
return;
}
void GfxText16::StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight) {
- Width(str, 0, (int16)strlen(str), orgFontId, textWidth, textHeight);
+ Width(str, 0, (int16)strlen(str), orgFontId, textWidth, textHeight, true);
}
void GfxText16::ShowString(const char *str, GuiResourceId orgFontId, int16 orgPenColor) {
@@ -300,14 +286,16 @@ void GfxText16::DrawString(const char *str, GuiResourceId orgFontId, int16 orgPe
}
int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) {
- GuiResourceId oldFontId = GetFontId();
- int16 oldPenColor = _ports->_curPort->penClr;
+ GuiResourceId previousFontId = GetFontId();
+ int16 previousPenColor = _ports->_curPort->penClr;
int16 charCount;
int16 maxTextWidth = 0, textWidth;
int16 totalHeight = 0, textHeight;
if (fontId != -1)
SetFont(fontId);
+ else
+ fontId = previousFontId;
if (g_sci->getLanguage() == Common::JA_JPN)
SwitchToFont900OnSjis(text);
@@ -315,7 +303,7 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId
rect.top = rect.left = 0;
if (maxWidth < 0) { // force output as single line
- StringWidth(text, oldFontId, textWidth, textHeight);
+ StringWidth(text, fontId, textWidth, textHeight);
rect.bottom = textHeight;
rect.right = textWidth;
} else {
@@ -324,10 +312,10 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId
rect.right = (maxWidth ? maxWidth : 192);
const char *curPos = text;
while (*curPos) {
- charCount = GetLongest(curPos, rect.right, oldFontId);
+ charCount = GetLongest(curPos, rect.right, fontId);
if (charCount == 0)
break;
- Width(curPos, 0, charCount, oldFontId, textWidth, textHeight);
+ Width(curPos, 0, charCount, fontId, textWidth, textHeight, false);
maxTextWidth = MAX(textWidth, maxTextWidth);
totalHeight += textHeight;
curPos += charCount;
@@ -337,8 +325,8 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId
rect.bottom = totalHeight;
rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth);
}
- SetFont(oldFontId);
- _ports->penColor(oldPenColor);
+ SetFont(previousFontId);
+ _ports->penColor(previousPenColor);
return rect.right;
}
@@ -403,12 +391,14 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex
int16 textWidth, maxTextWidth, textHeight, charCount;
int16 offset = 0;
int16 hline = 0;
- GuiResourceId orgFontId = GetFontId();
- int16 orgPenColor = _ports->_curPort->penClr;
+ GuiResourceId previousFontId = GetFontId();
+ int16 previousPenColor = _ports->_curPort->penClr;
bool doubleByteMode = false;
if (fontId != -1)
SetFont(fontId);
+ else
+ fontId = previousFontId;
if (g_sci->getLanguage() == Common::JA_JPN) {
if (SwitchToFont900OnSjis(text))
@@ -417,10 +407,10 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex
maxTextWidth = 0;
while (*text) {
- charCount = GetLongest(text, rect.width(), orgFontId);
+ charCount = GetLongest(text, rect.width(), fontId);
if (charCount == 0)
break;
- Width(text, 0, charCount, orgFontId, textWidth, textHeight);
+ Width(text, 0, charCount, fontId, textWidth, textHeight, true);
maxTextWidth = MAX<int16>(maxTextWidth, textWidth);
switch (alignment) {
case SCI_TEXT16_ALIGNMENT_RIGHT:
@@ -439,9 +429,9 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex
_ports->moveTo(rect.left + offset, rect.top + hline);
if (bshow) {
- Show(text, 0, charCount, orgFontId, orgPenColor);
+ Show(text, 0, charCount, fontId, previousPenColor);
} else {
- Draw(text, 0, charCount, orgFontId, orgPenColor);
+ Draw(text, 0, charCount, fontId, previousPenColor);
}
hline += textHeight;
@@ -449,15 +439,18 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex
if (*text == ' ')
text++; // skip over breaking space
}
- SetFont(orgFontId);
- _ports->penColor(orgPenColor);
+ SetFont(previousFontId);
+ _ports->penColor(previousPenColor);
if (doubleByteMode) {
- // kanji is written by pc98 rom to screen directly. Because of GetLongest() behaviour (not cutting off the last
- // char, that causes a new line), results in the script thinking that the text would need less space. The coordinate
- // adjustment in fontsjis.cpp handles the incorrect centering because of that and this code actually shows all of
- // the chars - if we don't do this, the scripts will only show most of the chars, but the last few pixels won't get
- // shown most of the time.
+ // Kanji is written by pc98 rom to screen directly. Because of
+ // GetLongest() behaviour (not cutting off the last char, that causes a
+ // new line), results in the script thinking that the text would need
+ // less space. The coordinate adjustment in fontsjis.cpp handles the
+ // incorrect centering because of that and this code actually shows all
+ // of the chars - if we don't do this, the scripts will only show most
+ // of the chars, but the last few pixels won't get shown most of the
+ // time.
Common::Rect kanjiRect = rect;
_ports->offsetRect(kanjiRect);
kanjiRect.left &= 0xFFC;
@@ -470,15 +463,16 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex
}
void GfxText16::Draw_String(const char *text) {
- GuiResourceId orgFontId = GetFontId();
- int16 orgPenColor = _ports->_curPort->penClr;
+ GuiResourceId previousFontId = GetFontId();
+ int16 previousPenColor = _ports->_curPort->penClr;
- Draw(text, 0, strlen(text), orgFontId, orgPenColor);
- SetFont(orgFontId);
- _ports->penColor(orgPenColor);
+ Draw(text, 0, strlen(text), previousFontId, previousPenColor);
+ SetFont(previousFontId);
+ _ports->penColor(previousPenColor);
}
-// Sierra did this in their PC98 interpreter only, they identify a text as being sjis and then switch to font 900
+// Sierra did this in their PC98 interpreter only, they identify a text as being
+// sjis and then switch to font 900
bool GfxText16::SwitchToFont900OnSjis(const char *text) {
byte firstChar = (*(const byte *)text++);
if (((firstChar >= 0x81) && (firstChar <= 0x9F)) || ((firstChar >= 0xE0) && (firstChar <= 0xEF))) {
@@ -488,4 +482,35 @@ bool GfxText16::SwitchToFont900OnSjis(const char *text) {
return false;
}
+void GfxText16::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
+ Common::Rect rect(0, 0, 0, 0);
+ Size(rect, text, font, maxWidth);
+ *textWidth = rect.width();
+ *textHeight = rect.height();
+}
+
+// Used SCI1+ for text codes
+void GfxText16::kernelTextFonts(int argc, reg_t *argv) {
+ int i;
+
+ delete _codeFonts;
+ _codeFontsCount = argc;
+ _codeFonts = new GuiResourceId[argc];
+ for (i = 0; i < argc; i++) {
+ _codeFonts[i] = (GuiResourceId)argv[i].toUint16();
+ }
+}
+
+// Used SCI1+ for text codes
+void GfxText16::kernelTextColors(int argc, reg_t *argv) {
+ int i;
+
+ delete _codeColors;
+ _codeColorsCount = argc;
+ _codeColors = new uint16[argc];
+ for (i = 0; i < argc; i++) {
+ _codeColors[i] = argv[i].toUint16();
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h
index 2885fc928b..9b8b6d9f19 100644
--- a/engines/sci/graphics/text16.h
+++ b/engines/sci/graphics/text16.h
@@ -48,14 +48,12 @@ public:
GfxFont *GetFont();
void SetFont(GuiResourceId fontId);
- void CodeSetFonts(int argc, reg_t *argv);
- void CodeSetColors(int argc, reg_t *argv);
int16 CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor);
void ClearChar(int16 chr);
int16 GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId);
- void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
+ void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont);
void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
void ShowString(const char *str, GuiResourceId orgFontId, int16 orgPenColor);
void DrawString(const char *str, GuiResourceId orgFontId, int16 orgPenColor);
@@ -67,6 +65,10 @@ public:
GfxFont *_font;
+ void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
+ void kernelTextFonts(int argc, reg_t *argv);
+ void kernelTextColors(int argc, reg_t *argv);
+
private:
void init();
bool SwitchToFont900OnSjis(const char *text);
diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp
index 1976326aa9..abb5e74cbd 100644
--- a/engines/sci/graphics/transitions.cpp
+++ b/engines/sci/graphics/transitions.cpp
@@ -31,15 +31,14 @@
#include "sci/sci.h"
#include "sci/engine/state.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/transitions.h"
namespace Sci {
-GfxTransitions::GfxTransitions(SciGui *gui, GfxScreen *screen, GfxPalette *palette, bool isVGA)
- : _gui(gui), _screen(screen), _palette(palette), _isVGA(isVGA) {
+GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA)
+ : _screen(screen), _palette(palette), _isVGA(isVGA) {
init();
}
@@ -159,7 +158,8 @@ void GfxTransitions::doit(Common::Rect picRect) {
}
if (_blackoutFlag) {
- // We need to find out what transition we are supposed to use for blackout
+ // We need to find out what transition we are supposed to use for
+ // blackout
translationEntry = translateNumber(_number, blackoutTransitionIDs);
if (translationEntry) {
doTransition(translationEntry->newId, true);
@@ -168,12 +168,15 @@ void GfxTransitions::doit(Common::Rect picRect) {
}
}
+ _palette->palVaryPrepareForTransition();
+
// Now we do the actual transition to the new screen
doTransition(_number, false);
if (picRect.bottom != _screen->getHeight()) {
// TODO: this is a workaround for lsl6 not showing menubar when playing
- // There is some new code in the sierra sci in ShowPic that seems to do something similar to this
+ // There is some new code in the sierra sci in ShowPic that seems to do
+ // something similar to this
_screen->copyToScreen();
g_system->updateScreen();
}
@@ -181,8 +184,8 @@ void GfxTransitions::doit(Common::Rect picRect) {
_screen->_picNotValid = 0;
}
-// This may get called twice, if blackoutFlag is set. It will get once called with blackoutFlag set and another time
-// with no blackoutFlag.
+// This may get called twice, if blackoutFlag is set. It will get once called
+// with blackoutFlag set and another time with no blackoutFlag.
void GfxTransitions::doTransition(int16 number, bool blackoutFlag) {
if (number != SCI_TRANSITIONS_FADEPALETTE) {
setNewPalette(blackoutFlag);
@@ -193,7 +196,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) {
verticalRollFromCenter(blackoutFlag);
break;
case SCI_TRANSITIONS_VERTICALROLL_TOCENTER:
- verticalRollFromCenter(blackoutFlag);
+ verticalRollToCenter(blackoutFlag);
break;
case SCI_TRANSITIONS_HORIZONTALROLL_FROMCENTER:
horizontalRollFromCenter(blackoutFlag);
@@ -277,7 +280,8 @@ void GfxTransitions::copyRectToScreen(const Common::Rect rect, bool blackoutFlag
}
}
-// Note: dont do too many steps in here, otherwise cpu will crap out because of the load
+// Note: don't do too many steps in here, otherwise cpu will crap out because of
+// the load
void GfxTransitions::fadeOut() {
byte oldPalette[4 * 256], workPalette[4 * 256];
int16 stepNr, colorNr;
@@ -291,23 +295,24 @@ void GfxTransitions::fadeOut() {
workPalette[colorNr * 4 + 2] = oldPalette[colorNr * 4 + 2] * stepNr / 100;
}
g_system->setPalette(workPalette + 4, 1, 254);
- _gui->wait(2);
+ g_sci->getEngineState()->wait(2);
}
}
-// Note: dont do too many steps in here, otherwise cpu will crap out because of the load
+// Note: don't do too many steps in here, otherwise cpu will crap out because of
+// the load
void GfxTransitions::fadeIn() {
int16 stepNr;
for (stepNr = 0; stepNr <= 100; stepNr += 10) {
_palette->kernelSetIntensity(1, 255, stepNr, true);
- _gui->wait(2);
+ g_sci->getEngineState()->wait(2);
}
}
-// pixelates the new picture over the old one - works against the whole screen
+// Pixelates the new picture over the old one - works against the whole screen.
// TODO: it seems this needs to get applied on _picRect only if possible
-void GfxTransitions::pixelation (bool blackoutFlag) {
+void GfxTransitions::pixelation(bool blackoutFlag) {
uint16 mask = 0x40, stepNr = 0;
Common::Rect pixelRect;
@@ -327,7 +332,7 @@ void GfxTransitions::pixelation (bool blackoutFlag) {
} while (mask != 0x40);
}
-// like pixelation but uses 8x8 blocks - works against the whole screen
+// Like pixelation but uses 8x8 blocks - works against the whole screen.
// TODO: it seems this needs to get applied on _picRect only if possible
void GfxTransitions::blocks(bool blackoutFlag) {
uint16 mask = 0x40, stepNr = 0;
@@ -349,7 +354,8 @@ void GfxTransitions::blocks(bool blackoutFlag) {
} while (mask != 0x40);
}
-// directly shows new screen starting up/down/left/right and going to the opposite direction - works on _picRect area only
+// Directly shows new screen starting up/down/left/right and going to the
+// opposite direction - works on _picRect area only
void GfxTransitions::straight(int16 number, bool blackoutFlag) {
int16 stepNr = 0;
Common::Rect newScreenRect = _picRect;
@@ -401,38 +407,39 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) {
}
}
-// scroll old screen (up/down/left/right) and insert new screen that way - works on _picRect area only
+void GfxTransitions::scrollCopyOldToScreen(Common::Rect screenRect, int16 x, int16 y) {
+ byte *oldScreenPtr = _oldScreen;
+ int16 screenWidth = _screen->getDisplayWidth();
+ if (_screen->getUpscaledHires()) {
+ _screen->adjustToUpscaledCoordinates(screenRect.top, screenRect.left);
+ _screen->adjustToUpscaledCoordinates(screenRect.bottom, screenRect.right);
+ _screen->adjustToUpscaledCoordinates(y, x);
+ }
+ oldScreenPtr += screenRect.left + screenRect.top * screenWidth;
+ g_system->copyRectToScreen(oldScreenPtr, screenWidth, x, y, screenRect.width(), screenRect.height());
+}
+
+// Scroll old screen (up/down/left/right) and insert new screen that way - works
+// on _picRect area only.
void GfxTransitions::scroll(int16 number) {
int16 screenWidth, screenHeight;
- byte *oldScreenPtr;
int16 stepNr = 0;
Common::Rect oldMoveRect = _picRect;
+ Common::Rect oldScreenRect = _picRect;
Common::Rect newMoveRect = _picRect;
Common::Rect newScreenRect = _picRect;
_screen->copyFromScreen(_oldScreen);
screenWidth = _screen->getDisplayWidth(); screenHeight = _screen->getDisplayHeight();
- oldScreenPtr = _oldScreen + _picRect.left + _picRect.top * screenWidth;
- if (_screen->getUpscaledHires()) {
- oldScreenPtr += _picRect.left + _picRect.top * screenWidth;
- }
-
switch (number) {
case SCI_TRANSITIONS_SCROLL_LEFT:
newScreenRect.right = newScreenRect.left;
newMoveRect.left = newMoveRect.right;
while (oldMoveRect.left < oldMoveRect.right) {
- oldScreenPtr++;
- if (_screen->getUpscaledHires())
- oldScreenPtr++;
- oldMoveRect.right--;
- if (oldMoveRect.right > oldMoveRect.left) {
- if (!_screen->getUpscaledHires())
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height());
- else
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left * 2, oldMoveRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2);
- }
+ oldMoveRect.right--; oldScreenRect.left++;
+ if (oldMoveRect.right > oldMoveRect.left)
+ scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top);
newScreenRect.right++; newMoveRect.left--;
_screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
if ((stepNr & 1) == 0) {
@@ -440,20 +447,20 @@ void GfxTransitions::scroll(int16 number) {
}
stepNr++;
}
- if ((stepNr & 1) == 0)
- g_system->updateScreen();
+ if ((stepNr & 1) == 0) {
+ if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) {
+ g_system->updateScreen();
+ g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis();
+ }
+ }
break;
case SCI_TRANSITIONS_SCROLL_RIGHT:
newScreenRect.left = newScreenRect.right;
while (oldMoveRect.left < oldMoveRect.right) {
- oldMoveRect.left++;
- if (oldMoveRect.right > oldMoveRect.left) {
- if (!_screen->getUpscaledHires())
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height());
- else
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left * 2, oldMoveRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2);
- }
+ oldMoveRect.left++; oldScreenRect.right--;
+ if (oldMoveRect.right > oldMoveRect.left)
+ scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top);
newScreenRect.left--;
_screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
if ((stepNr & 1) == 0) {
@@ -461,24 +468,21 @@ void GfxTransitions::scroll(int16 number) {
}
stepNr++;
}
- if ((stepNr & 1) == 0)
- g_system->updateScreen();
+ if ((stepNr & 1) == 0) {
+ if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) {
+ g_system->updateScreen();
+ g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis();
+ }
+ }
break;
case SCI_TRANSITIONS_SCROLL_UP:
newScreenRect.bottom = newScreenRect.top;
newMoveRect.top = newMoveRect.bottom;
while (oldMoveRect.top < oldMoveRect.bottom) {
- oldScreenPtr += screenWidth;
- if (_screen->getUpscaledHires())
- oldScreenPtr += screenWidth;
- oldMoveRect.top++;
- if (oldMoveRect.top < oldMoveRect.bottom) {
- if (!_screen->getUpscaledHires())
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, _picRect.left, _picRect.top, oldMoveRect.width(), oldMoveRect.height());
- else
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, _picRect.left * 2, _picRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2);
- }
+ oldMoveRect.top++; oldScreenRect.top++;
+ if (oldMoveRect.top < oldMoveRect.bottom)
+ scrollCopyOldToScreen(oldScreenRect, _picRect.left, _picRect.top);
newScreenRect.bottom++; newMoveRect.top--;
_screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
updateScreenAndWait(3);
@@ -488,13 +492,9 @@ void GfxTransitions::scroll(int16 number) {
case SCI_TRANSITIONS_SCROLL_DOWN:
newScreenRect.top = newScreenRect.bottom;
while (oldMoveRect.top < oldMoveRect.bottom) {
- oldMoveRect.top++;
- if (oldMoveRect.top < oldMoveRect.bottom) {
- if (!_screen->getUpscaledHires())
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height());
- else
- g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left * 2, oldMoveRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2);
- }
+ oldMoveRect.top++; oldScreenRect.bottom--;
+ if (oldMoveRect.top < oldMoveRect.bottom)
+ scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top);
newScreenRect.top--;
_screen->copyRectToScreen(newScreenRect, _picRect.left, _picRect.top);
updateScreenAndWait(3);
@@ -503,7 +503,8 @@ void GfxTransitions::scroll(int16 number) {
}
}
-// vertically displays new screen starting from center - works on _picRect area only
+// Vertically displays new screen starting from center - works on _picRect area
+// only
void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) {
Common::Rect leftRect = Common::Rect(_picRect.left + (_picRect.width() / 2) -1, _picRect.top, _picRect.left + (_picRect.width() / 2), _picRect.bottom);
Common::Rect rightRect = Common::Rect(leftRect.right, _picRect.top, leftRect.right + 1, _picRect.bottom);
@@ -519,10 +520,11 @@ void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) {
}
}
-// vertically displays new screen starting from edges - works on _picRect area only
+// Vertically displays new screen starting from edges - works on _picRect area
+// only
void GfxTransitions::verticalRollToCenter(bool blackoutFlag) {
Common::Rect leftRect = Common::Rect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom);
- Common::Rect rightRect = Common::Rect(leftRect.right - 1, _picRect.top, leftRect.right, _picRect.bottom);
+ Common::Rect rightRect = Common::Rect(_picRect.right - 1, _picRect.top, _picRect.right, _picRect.bottom);
while (leftRect.left < rightRect.right) {
copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(1, 0);
@@ -531,7 +533,8 @@ void GfxTransitions::verticalRollToCenter(bool blackoutFlag) {
}
}
-// horizontally displays new screen starting from center - works on _picRect area only
+// Horizontally displays new screen starting from center - works on _picRect
+// area only
void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) {
Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top + (_picRect.height() / 2) - 1, _picRect.right, _picRect.top + (_picRect.height() / 2));
Common::Rect lowerRect = Common::Rect(upperRect.left, upperRect.bottom, upperRect.right, upperRect.bottom + 1);
@@ -547,7 +550,8 @@ void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) {
}
}
-// horizontally displays new screen starting from upper and lower edge - works on _picRect area only
+// Horizontally displays new screen starting from upper and lower edge - works
+// on _picRect area only
void GfxTransitions::horizontalRollToCenter(bool blackoutFlag) {
Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top, _picRect.right, _picRect.top + 1);
Common::Rect lowerRect = Common::Rect(upperRect.left, _picRect.bottom - 1, upperRect.right, _picRect.bottom);
@@ -559,8 +563,8 @@ void GfxTransitions::horizontalRollToCenter(bool blackoutFlag) {
}
}
-// diagonally displays new screen starting from center - works on _picRect area only
-// assumes that height of rect is larger than width
+// Diagonally displays new screen starting from center - works on _picRect area
+// only. Assumes that height of rect is larger than width.
void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) {
int16 halfHeight = _picRect.height() / 2;
Common::Rect upperRect(_picRect.left + halfHeight - 2, _picRect.top + halfHeight, _picRect.right - halfHeight + 1, _picRect.top + halfHeight + 1);
@@ -589,8 +593,8 @@ void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) {
}
}
-// diagonally displays new screen starting from edges - works on _picRect area only
-// assumes that height of rect is larger than width
+// Diagonally displays new screen starting from edges - works on _picRect area
+// only. Assumes that height of rect is larger than width.
void GfxTransitions::diagonalRollToCenter(bool blackoutFlag) {
Common::Rect upperRect(_picRect.left, _picRect.top, _picRect.right, _picRect.top + 1);
Common::Rect lowerRect(_picRect.left, _picRect.bottom - 1, _picRect.right, _picRect.bottom);
diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h
index 9a1a412d5b..233638ffda 100644
--- a/engines/sci/graphics/transitions.h
+++ b/engines/sci/graphics/transitions.h
@@ -65,7 +65,7 @@ class Screen;
*/
class GfxTransitions {
public:
- GfxTransitions(SciGui *gui, GfxScreen *screen, GfxPalette *palette, bool isVGA);
+ GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA);
~GfxTransitions();
void setup(int16 number, bool blackoutFlag);
@@ -83,6 +83,7 @@ private:
void pixelation(bool blackoutFlag);
void blocks(bool blackoutFlag);
void straight(int16 number, bool blackoutFlag);
+ void scrollCopyOldToScreen(Common::Rect screenRect, int16 x, int16 y);
void scroll(int16 number);
void verticalRollFromCenter(bool blackoutFlag);
void verticalRollToCenter(bool blackoutFlag);
@@ -92,7 +93,6 @@ private:
void diagonalRollToCenter(bool blackoutFlag);
void updateScreenAndWait(int msec);
- SciGui *_gui;
GfxScreen *_screen;
GfxPalette *_palette;
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 2ba14fbd8f..1c865f6bcf 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -28,6 +28,7 @@
#include "sci/engine/state.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/view.h"
namespace Sci {
@@ -35,6 +36,7 @@ namespace Sci {
GfxView::GfxView(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId)
: _resMan(resMan), _screen(screen), _palette(palette), _resourceId(resourceId) {
assert(resourceId != -1);
+ _coordAdjuster = g_sci->_gfxCoordAdjuster;
initData(resourceId);
}
@@ -62,6 +64,7 @@ void GfxView::initData(GuiResourceId resourceId) {
error("view resource %d not found", resourceId);
}
_resourceData = _resource->data;
+ _resourceSize = _resource->size;
byte *celData, *loopData;
uint16 celOffset;
@@ -75,12 +78,32 @@ void GfxView::initData(GuiResourceId resourceId) {
byte seekEntry;
bool isEGA = false;
bool isCompressed = true;
+ ViewType curViewType = _resMan->getViewType();
_loopCount = 0;
_embeddedPal = false;
_EGAmapping = NULL;
+ _isSci2Hires = false;
+ _isScaleable = true;
+
+ // we adjust inside getCelRect for SCI0EARLY (that version didn't have the +1 when calculating bottom)
+ _adjustForSci0Early = getSciVersion() == SCI_VERSION_0_EARLY ? -1 : 0;
+
+ // If we find an SCI1/SCI1.1 view (not amiga), we switch to that type for
+ // EGA. This could get used to make view patches for EGA games, where the
+ // new views include more colors. Users could manually adjust old views to
+ // make them look better (like removing dithered colors that aren't caught
+ // by our undithering or even improve the graphics overall).
+ if (curViewType == kViewEga) {
+ if (_resourceData[1] == 0x80) {
+ curViewType = kViewVga;
+ } else {
+ if (READ_LE_UINT16(_resourceData + 4) == 1)
+ curViewType = kViewVga11;
+ }
+ }
- switch (_resMan->getViewType()) {
+ switch (curViewType) {
case kViewEga: // View-format SCI0 (and Amiga 16 colors)
isEGA = true;
case kViewAmiga: // View-format Amiga (32 colors)
@@ -95,19 +118,21 @@ void GfxView::initData(GuiResourceId resourceId) {
palOffset = READ_LE_UINT16(_resourceData + 6);
if (palOffset && palOffset != 0x100) {
- // Some SCI0/SCI01 games also have an offset set. It seems that it points to a 16-byte mapping table
- // but on those games using that mapping will actually screw things up.
- // On the other side: vga sci1 games have this pointing to a VGA palette
- // and ega sci1 games have this pointing to a 8x16 byte mapping table that needs to get applied then
+ // Some SCI0/SCI01 games also have an offset set. It seems that it
+ // points to a 16-byte mapping table but on those games using that
+ // mapping will actually screw things up. On the other side: VGA
+ // SCI1 games have this pointing to a VGA palette and EGA SCI1 games
+ // have this pointing to a 8x16 byte mapping table that needs to get
+ // applied then.
if (!isEGA) {
- _palette->createFromData(&_resourceData[palOffset], &_viewPalette);
+ _palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
_embeddedPal = true;
} else {
// Only use the EGA-mapping, when being SCI1
if (getSciVersion() >= SCI_VERSION_1_EGA) {
_EGAmapping = &_resourceData[palOffset];
for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) {
- if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE)!=0)
+ if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0)
break;
_EGAmapping += SCI_VIEW_EGAMAPPING_SIZE;
}
@@ -141,8 +166,8 @@ void GfxView::initData(GuiResourceId resourceId) {
// For EGA
// Width:WORD Height:WORD DisplaceX:BYTE DisplaceY:BYTE ClearKey:BYTE EGAData starts now directly
cel = &_loop[loopNo].cel[celNo];
- cel->width = READ_LE_UINT16(celData);
- cel->height = READ_LE_UINT16(celData + 2);
+ cel->scriptWidth = cel->width = READ_LE_UINT16(celData);
+ cel->scriptHeight = cel->height = READ_LE_UINT16(celData + 2);
cel->displaceX = (signed char)celData[4];
cel->displaceY = celData[5];
cel->clearKey = celData[6];
@@ -168,13 +193,30 @@ void GfxView::initData(GuiResourceId resourceId) {
break;
case kViewVga11: // View-format SCI1.1+
- // HeaderSize:WORD LoopCount:BYTE Unknown:BYTE Version:WORD Unknown:WORD PaletteOffset:WORD
- headerSize = READ_SCI11ENDIAN_UINT16(_resourceData + 0) + 2; // headerSize is not part of the header, so its added
+ // HeaderSize:WORD LoopCount:BYTE Flags:BYTE Version:WORD Unknown:WORD PaletteOffset:WORD
+ headerSize = READ_SCI11ENDIAN_UINT16(_resourceData + 0) + 2; // headerSize is not part of the header, so it's added
assert(headerSize >= 16);
_loopCount = _resourceData[2];
assert(_loopCount);
+ _isSci2Hires = _resourceData[5] == 1 ? true : false;
palOffset = READ_SCI11ENDIAN_UINT32(_resourceData + 8);
- // FIXME: After LoopCount there is another byte and its set for view 50 within Laura Bow 2 CD, check what it means
+ // flags is actually a bit-mask
+ // it seems it was only used for some early sci1.1 games (or even just laura bow 2)
+ // later interpreters dont support it at all anymore
+ // we assume that if flags is 0h the view does not support flags and default to scaleable
+ // if it's 1h then we assume that the view is not to be scaled
+ // if it's 40h then we assume that the view is scaleable
+ switch (_resourceData[3]) {
+ case 1:
+ _isScaleable = false;
+ break;
+ case 0x40:
+ case 0:
+ break; // don't do anything, we already have _isScaleable set
+ default:
+ error("unsupported flags byte inside sci1.1 view");
+ break;
+ }
loopData = _resourceData + headerSize;
loopSize = _resourceData[12];
@@ -183,7 +225,7 @@ void GfxView::initData(GuiResourceId resourceId) {
assert(celSize >= 32);
if (palOffset) {
- _palette->createFromData(&_resourceData[palOffset], &_viewPalette);
+ _palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
_embeddedPal = true;
}
@@ -210,8 +252,8 @@ void GfxView::initData(GuiResourceId resourceId) {
_loop[loopNo].cel = new CelInfo[celCount];
for (celNo = 0; celNo < celCount; celNo++) {
cel = &_loop[loopNo].cel[celNo];
- cel->width = READ_SCI11ENDIAN_UINT16(celData);
- cel->height = READ_SCI11ENDIAN_UINT16(celData + 2);
+ cel->scriptWidth = cel->width = READ_SCI11ENDIAN_UINT16(celData);
+ cel->scriptHeight = cel->height = READ_SCI11ENDIAN_UINT16(celData + 2);
cel->displaceX = READ_SCI11ENDIAN_UINT16(celData + 4);
cel->displaceY = READ_SCI11ENDIAN_UINT16(celData + 6);
@@ -221,6 +263,9 @@ void GfxView::initData(GuiResourceId resourceId) {
cel->offsetEGA = 0;
cel->offsetRLE = READ_SCI11ENDIAN_UINT32(celData + 24);
cel->offsetLiteral = READ_SCI11ENDIAN_UINT32(celData + 28);
+ // GK1-hires content is actually uncompressed, we need to swap both so that we process it as such
+ if ((cel->offsetRLE) && (!cel->offsetLiteral))
+ SWAP(cel->offsetRLE, cel->offsetLiteral);
cel->rawBitmap = 0;
if (_loop[loopNo].mirrorFlag)
@@ -229,6 +274,29 @@ void GfxView::initData(GuiResourceId resourceId) {
celData += celSize;
}
}
+#ifdef ENABLE_SCI32
+ // adjust width/height returned to scripts
+ switch (getSciVersion()) {
+ case SCI_VERSION_2:
+ if (_isSci2Hires) {
+ for (loopNo = 0; loopNo < _loopCount; loopNo++) {
+ for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) {
+ _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight);
+ }
+ }
+ }
+ break;
+
+ case SCI_VERSION_2_1:
+ for (loopNo = 0; loopNo < _loopCount; loopNo++) {
+ for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) {
+ _coordAdjuster->fromDisplayToScript(_loop[loopNo].cel[celNo].scriptHeight, _loop[loopNo].cel[celNo].scriptWidth);
+ }
+ }
+ default:
+ break;
+ }
+#endif
break;
default:
@@ -236,65 +304,72 @@ void GfxView::initData(GuiResourceId resourceId) {
}
}
-GuiResourceId GfxView::getResourceId() {
+GuiResourceId GfxView::getResourceId() const {
return _resourceId;
}
-int16 GfxView::getWidth(int16 loopNo, int16 celNo) {
- loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
- celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
- return _loopCount ? _loop[loopNo].cel[celNo].width : 0;
+int16 GfxView::getWidth(int16 loopNo, int16 celNo) const {
+ return _loopCount ? getCelInfo(loopNo, celNo)->width : 0;
}
-int16 GfxView::getHeight(int16 loopNo, int16 celNo) {
- loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
- celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
- return _loopCount ? _loop[loopNo].cel[celNo].height : 0;
+int16 GfxView::getHeight(int16 loopNo, int16 celNo) const {
+ return _loopCount ? getCelInfo(loopNo, celNo)->height : 0;
}
-CelInfo *GfxView::getCelInfo(int16 loopNo, int16 celNo) {
+const CelInfo *GfxView::getCelInfo(int16 loopNo, int16 celNo) const {
+ assert(_loopCount);
loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
- return _loopCount ? &_loop[loopNo].cel[celNo] : NULL;
+ return &_loop[loopNo].cel[celNo];
}
-LoopInfo *GfxView::getLoopInfo(int16 loopNo) {
+uint16 GfxView::getCelCount(int16 loopNo) const {
+ assert(_loopCount);
loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
- return _loopCount ? &_loop[loopNo] : NULL;
+ return _loop[loopNo].celCount;
}
-void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect *outRect) {
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- if (celInfo) {
- outRect->left = x + celInfo->displaceX - (celInfo->width >> 1);
- outRect->right = outRect->left + celInfo->width;
- outRect->bottom = y + celInfo->displaceY - z + 1;
- outRect->top = outRect->bottom - celInfo->height;
- }
+Palette *GfxView::getPalette() {
+ return _embeddedPal ? &_viewPalette : NULL;
}
-void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect *outRect) {
+bool GfxView::isSci2Hires() {
+ return _isSci2Hires;
+}
+
+bool GfxView::isScaleable() {
+ return _isScaleable;
+}
+
+void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const {
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ outRect.left = x + celInfo->displaceX - (celInfo->width >> 1);
+ outRect.right = outRect.left + celInfo->width;
+ outRect.bottom = y + celInfo->displaceY - z + 1 + _adjustForSci0Early;
+ outRect.top = outRect.bottom - celInfo->height;
+}
+
+void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const {
int16 scaledDisplaceX, scaledDisplaceY;
int16 scaledWidth, scaledHeight;
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- if (celInfo) {
- // Scaling displaceX/Y, Width/Height
- scaledDisplaceX = (celInfo->displaceX * scaleX) >> 7;
- scaledDisplaceY = (celInfo->displaceY * scaleY) >> 7;
- scaledWidth = (celInfo->width * scaleX) >> 7;
- scaledHeight = (celInfo->height * scaleY) >> 7;
- scaledWidth = CLIP<int16>(scaledWidth, 0, _screen->getWidth());
- scaledHeight = CLIP<int16>(scaledHeight, 0, _screen->getHeight());
-
- outRect->left = x + scaledDisplaceX - (scaledWidth >> 1);
- outRect->right = outRect->left + scaledWidth;
- outRect->bottom = y + scaledDisplaceY - z + 1;
- outRect->top = outRect->bottom - scaledHeight;
- }
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+
+ // Scaling displaceX/Y, Width/Height
+ scaledDisplaceX = (celInfo->displaceX * scaleX) >> 7;
+ scaledDisplaceY = (celInfo->displaceY * scaleY) >> 7;
+ scaledWidth = (celInfo->width * scaleX) >> 7;
+ scaledHeight = (celInfo->height * scaleY) >> 7;
+ scaledWidth = CLIP<int16>(scaledWidth, 0, _screen->getWidth());
+ scaledHeight = CLIP<int16>(scaledHeight, 0, _screen->getHeight());
+
+ outRect.left = x + scaledDisplaceX - (scaledWidth >> 1);
+ outRect.right = outRect.left + scaledWidth;
+ outRect.bottom = y + scaledDisplaceY - z + 1;
+ outRect.top = outRect.bottom - scaledHeight;
}
void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) {
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
byte *rlePtr;
byte *literalPtr;
uint32 pixelNo = 0, runLength;
@@ -309,78 +384,42 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
memset(outPtr + pixelNo, pixel & 0x0F, MIN<uint32>(runLength, pixelCount - pixelNo));
pixelNo += runLength;
}
- return;
- }
-
- rlePtr = _resourceData + celInfo->offsetRLE;
- if (!celInfo->offsetLiteral) { // no additional literal data
- if (_resMan->isAmiga32color()) {
- // decompression for amiga views
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- if (pixel & 0x07) { // fill with color
- runLength = pixel & 0x07;
- pixel = pixel >> 3;
- while (runLength-- && pixelNo < pixelCount) {
- outPtr[pixelNo++] = pixel;
- }
- } else { // fill with transparent
- runLength = pixel >> 3;
- pixelNo += runLength;
- }
- }
- return;
- } else {
- // decompression for data that has just one combined stream
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- runLength = pixel & 0x3F;
- switch (pixel & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *rlePtr++;
- break;
- case 0x80: // fill with color
- memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNo += runLength;
- break;
- }
- }
- return;
- }
} else {
- literalPtr = _resourceData + celInfo->offsetLiteral;
- if (celInfo->offsetRLE) {
- if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
- // Crazy-Ass compression for SCI1.1+ Mac
+ // We fill the buffer with transparent pixels, so that we can later skip
+ // over pixels to automatically have them transparent
+ // Also some RLE compressed cels are possibly ending with the last
+ // non-transparent pixel (is this even possible with the current code?)
+ memset(outPtr, _loop[loopNo].cel[celNo].clearKey, pixelCount);
+
+ rlePtr = _resourceData + celInfo->offsetRLE;
+ if (!celInfo->offsetLiteral) { // no additional literal data
+ if (_resMan->isAmiga32color()) {
+ // decompression for amiga views
while (pixelNo < pixelCount) {
- uint32 pixelLine = pixelNo;
- runLength = *rlePtr++;
- pixelNo += runLength;
- runLength = *rlePtr++;
- while (runLength-- && pixelNo < pixelCount) {
- outPtr[pixelNo] = *literalPtr++;
- if (outPtr[pixelNo] == 255)
- outPtr[pixelNo] = 0;
- pixelNo++;
+ pixel = *rlePtr++;
+ if (pixel & 0x07) { // fill with color
+ runLength = pixel & 0x07;
+ pixel = pixel >> 3;
+ while (runLength-- && pixelNo < pixelCount) {
+ outPtr[pixelNo++] = pixel;
+ }
+ } else { // fill with transparent
+ runLength = pixel >> 3;
+ pixelNo += runLength;
}
- pixelNo = pixelLine + celInfo->width;
}
} else {
- // decompression for data that has separate rle and literal streams
+ // decompression for data that has just one combined stream
while (pixelNo < pixelCount) {
pixel = *rlePtr++;
runLength = pixel & 0x3F;
switch (pixel & 0xC0) {
case 0: // copy bytes as-is
while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *literalPtr++;
+ outPtr[pixelNo++] = *rlePtr++;
break;
case 0x80: // fill with color
- memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
+ memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
pixelNo += runLength;
break;
case 0xC0: // fill with transparent
@@ -390,15 +429,53 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
}
}
} else {
- // literal stream only, so no compression
- memcpy(outPtr, literalPtr, pixelCount);
+ literalPtr = _resourceData + celInfo->offsetLiteral;
+ if (celInfo->offsetRLE) {
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
+ // compression for SCI1.1+ Mac
+ while (pixelNo < pixelCount) {
+ uint32 pixelLine = pixelNo;
+ runLength = *rlePtr++;
+ pixelNo += runLength;
+ runLength = *rlePtr++;
+ while (runLength-- && pixelNo < pixelCount) {
+ outPtr[pixelNo] = *literalPtr++;
+ if (outPtr[pixelNo] == 255)
+ outPtr[pixelNo] = 0;
+ pixelNo++;
+ }
+ pixelNo = pixelLine + celInfo->width;
+ }
+ } else {
+ // decompression for data that has separate rle and literal streams
+ while (pixelNo < pixelCount) {
+ pixel = *rlePtr++;
+ runLength = pixel & 0x3F;
+ switch (pixel & 0xC0) {
+ case 0: // copy bytes as-is
+ while (runLength-- && pixelNo < pixelCount)
+ outPtr[pixelNo++] = *literalPtr++;
+ break;
+ case 0x80: // fill with color
+ memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
+ pixelNo += runLength;
+ break;
+ case 0xC0: // fill with transparent
+ pixelNo += runLength;
+ break;
+ }
+ }
+ }
+ } else {
+ // literal stream only, so no compression
+ memcpy(outPtr, literalPtr, pixelCount);
+ pixelNo = pixelCount;
+ }
}
- return;
}
- error("Unable to decompress view");
}
-byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
+const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
if (_loop[loopNo].cel[celNo].rawBitmap)
@@ -411,9 +488,7 @@ byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
_loop[loopNo].cel[celNo].rawBitmap = new byte[pixelCount];
byte *pBitmap = _loop[loopNo].cel[celNo].rawBitmap;
- // Some RLE compressed cels end with the last non-transparent pixel, thats why we fill it up here
- // FIXME: change this to fill the remaining bytes within unpackCel()
- memset(pBitmap, _loop[loopNo].cel[celNo].clearKey, pixelCount);
+ // unpack the actual cel bitmap data
unpackCel(loopNo, celNo, pBitmap, pixelCount);
if (!_resMan->isVGA()) {
@@ -429,17 +504,21 @@ byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
return _loop[loopNo].cel[celNo].rawBitmap;
}
-// Called after unpacking an EGA cel, this will try to undither (parts) of the cel if the dithering in here
-// matches dithering used by the current picture
+/**
+ * Called after unpacking an EGA cel, this will try to undither (parts) of the
+ * cel if the dithering in here matches dithering used by the current picture.
+ */
void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) {
int16 *unditherMemorial = _screen->unditherGetMemorial();
- // It makes no sense to go further, if no memorial data from current picture is available
+ // It makes no sense to go further, if no memorial data from current picture
+ // is available
if (!unditherMemorial)
return;
// Makes no sense to process bitmaps that are 3 pixels wide or less
- if (width <= 3) return;
+ if (width <= 3)
+ return;
// If EGA mapping is used for this view, dont do undithering as well
if (_EGAmapping)
@@ -453,7 +532,8 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
memset(&bitmapMemorial, 0, sizeof(bitmapMemorial));
- // Count all seemingly dithered pixel-combinations as soon as at least 4 pixels are adjacent
+ // Count all seemingly dithered pixel-combinations as soon as at least 4
+ // pixels are adjacent
curPtr = bitmapPtr;
for (y = 0; y < height; y++) {
color1 = curPtr[0]; color2 = (curPtr[1] << 4) | curPtr[2];
@@ -466,17 +546,20 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
}
}
- // Now compare both memorial tables to find out matching dithering-combinations
+ // Now compare both memorial tables to find out matching
+ // dithering-combinations
bool unditherTable[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
byte color, unditherCount = 0;
memset(&unditherTable, false, sizeof(unditherTable));
for (color = 0; color < 255; color++) {
if ((bitmapMemorial[color] > 5) && (unditherMemorial[color] > 200)) {
- // match found, check if colorKey is contained -> if so, we ignore of course
+ // match found, check if colorKey is contained -> if so, we ignore
+ // of course
color1 = color & 0x0F; color2 = color >> 4;
if ((color1 != clearKey) && (color2 != clearKey) && (color1 != color2)) {
// so set this and the reversed color-combination for undithering
- unditherTable[color] = true; unditherTable[(color1 << 4) | color2] = true;
+ unditherTable[color] = true;
+ unditherTable[(color1 << 4) | color2] = true;
unditherCount++;
}
}
@@ -493,8 +576,9 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
for (x = 1; x < width; x++) {
color = (color << 4) | curPtr[1];
if (unditherTable[color]) {
- // some color with black? turn colors around otherwise it wont be the right color at all
- if ((color & 0xF0)==0)
+ // Some color with black? Turn colors around, otherwise it won't
+ // be the right color at all.
+ if ((color & 0xF0) == 0)
color = (color << 4) | (color >> 4);
curPtr[0] = color; curPtr[1] = color;
}
@@ -504,15 +588,15 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
}
}
-void GfxView::draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires) {
- Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- byte *bitmap = getBitmap(loopNo, celNo);
- int16 celHeight = celInfo->height, celWidth = celInfo->width;
- int16 width, height;
- byte clearKey = celInfo->clearKey;
- byte color;
- byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
+void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated,
+ int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires) {
+ const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ const byte *bitmap = getBitmap(loopNo, celNo);
+ const int16 celHeight = celInfo->height;
+ const int16 celWidth = celInfo->width;
+ const byte clearKey = celInfo->clearKey;
+ const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
int x, y;
if (_embeddedPal) {
@@ -520,24 +604,27 @@ void GfxView::draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRe
_palette->set(&_viewPalette, false);
}
- width = MIN(clipRect.width(), celWidth);
- height = MIN(clipRect.height(), celHeight);
+ const int16 width = MIN(clipRect.width(), celWidth);
+ const int16 height = MIN(clipRect.height(), celHeight);
bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
if (!_EGAmapping) {
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
- color = bitmap[x];
+ const byte color = bitmap[x];
if (color != clearKey) {
+ const int x2 = clipRectTranslated.left + x;
+ const int y2 = clipRectTranslated.top + y;
if (!upscaledHires) {
- if (priority >= _screen->getPriority(clipRectTranslated.left + x, clipRectTranslated.top + y))
- _screen->putPixel(clipRectTranslated.left + x, clipRectTranslated.top + y, drawMask, palette->mapping[color], priority, 0);
+ if (priority >= _screen->getPriority(x2, y2))
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
} else {
- // UpscaledHires means view is hires and is supposed to get drawn onto lowres screen
- // FIXME(?): we can't read priority directly with the hires coordinates. may not be needed at all
- // in kq6
- _screen->putPixelOnDisplay(clipRectTranslated.left + x, clipRectTranslated.top + y, palette->mapping[color]);
+ // UpscaledHires means view is hires and is supposed to
+ // get drawn onto lowres screen.
+ // FIXME(?): we can't read priority directly with the
+ // hires coordinates. may not be needed at all in kq6
+ _screen->putPixelOnDisplay(x2, y2, palette->mapping[color]);
}
}
}
@@ -546,30 +633,34 @@ void GfxView::draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRe
byte *EGAmapping = _EGAmapping + (EGAmappingNr * SCI_VIEW_EGAMAPPING_SIZE);
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
- color = EGAmapping[bitmap[x]];
- if (color != clearKey && priority >= _screen->getPriority(clipRectTranslated.left + x, clipRectTranslated.top + y))
- _screen->putPixel(clipRectTranslated.left + x, clipRectTranslated.top + y, drawMask, color, priority, 0);
+ const byte color = EGAmapping[bitmap[x]];
+ const int x2 = clipRectTranslated.left + x;
+ const int y2 = clipRectTranslated.top + y;
+ if (color != clearKey && priority >= _screen->getPriority(x2, y2))
+ _screen->putPixel(x2, y2, drawMask, color, priority, 0);
}
}
}
}
-// We don't fully follow sierra sci here, I did the scaling algo myself and it's definitely not pixel-perfect
-// with the one sierra is using. It shouldn't matter because the scaled cel rect is definitely the same as in sierra sci
-void GfxView::drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY) {
- Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- byte *bitmap = getBitmap(loopNo, celNo);
- int16 celHeight = celInfo->height, celWidth = celInfo->width;
- byte clearKey = celInfo->clearKey;
- byte color;
- byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
- int x, y;
- uint16 scalingX[320];
- uint16 scalingY[200];
+/**
+ * We don't fully follow sierra sci here, I did the scaling algo myself and it
+ * is definitely not pixel-perfect with the one sierra is using. It shouldn't
+ * matter because the scaled cel rect is definitely the same as in sierra sci.
+ */
+void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated,
+ int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY) {
+ const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ const byte *bitmap = getBitmap(loopNo, celNo);
+ const int16 celHeight = celInfo->height;
+ const int16 celWidth = celInfo->width;
+ const byte clearKey = celInfo->clearKey;
+ const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
+ uint16 scalingX[640];
+ uint16 scalingY[480];
int16 scaledWidth, scaledHeight;
- int16 pixelNo, scaledPixel, scaledPixelNo, prevScaledPixelNo;
- uint16 offsetX, offsetY;
+ int pixelNo, scaledPixel, scaledPixelNo, prevScaledPixelNo;
if (_embeddedPal) {
// Merge view palette in...
@@ -582,65 +673,63 @@ void GfxView::drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect
scaledHeight = CLIP<int16>(scaledHeight, 0, _screen->getHeight());
// Do we really need to do this?!
- memset(scalingX, 0, sizeof(scalingX));
- memset(scalingY, 0, sizeof(scalingY));
+ //memset(scalingX, 0, sizeof(scalingX));
+ //memset(scalingY, 0, sizeof(scalingY));
// Create height scaling table
pixelNo = 0;
scaledPixel = scaledPixelNo = prevScaledPixelNo = 0;
while (pixelNo < celHeight) {
scaledPixelNo = scaledPixel >> 7;
- if (prevScaledPixelNo < scaledPixelNo)
- memset(&scalingY[prevScaledPixelNo], pixelNo, scaledPixelNo - prevScaledPixelNo);
- scalingY[scaledPixelNo] = pixelNo;
- prevScaledPixelNo = scaledPixelNo + 1;
+ assert(scaledPixelNo < ARRAYSIZE(scalingY));
+ for (; prevScaledPixelNo <= scaledPixelNo; prevScaledPixelNo++)
+ scalingY[prevScaledPixelNo] = pixelNo;
pixelNo++;
scaledPixel += scaleY;
}
+ pixelNo--;
scaledPixelNo++;
- if (scaledPixelNo < scaledHeight)
- memset(&scalingY[scaledPixelNo], pixelNo - 1, scaledHeight - scaledPixelNo);
+ for (; scaledPixelNo < scaledHeight; scaledPixelNo++)
+ scalingY[scaledPixelNo] = pixelNo;
// Create width scaling table
pixelNo = 0;
scaledPixel = scaledPixelNo = prevScaledPixelNo = 0;
while (pixelNo < celWidth) {
scaledPixelNo = scaledPixel >> 7;
- if (prevScaledPixelNo < scaledPixelNo)
- memset(&scalingX[prevScaledPixelNo], pixelNo, scaledPixelNo - prevScaledPixelNo);
- scalingX[scaledPixelNo] = pixelNo;
- prevScaledPixelNo = scaledPixelNo + 1;
+ assert(scaledPixelNo < ARRAYSIZE(scalingX));
+ for (; prevScaledPixelNo <= scaledPixelNo; prevScaledPixelNo++)
+ scalingX[prevScaledPixelNo] = pixelNo;
pixelNo++;
scaledPixel += scaleX;
}
+ pixelNo--;
scaledPixelNo++;
- if (scaledPixelNo < scaledWidth)
- memset(&scalingX[scaledPixelNo], pixelNo - 1, scaledWidth - scaledPixelNo);
+ for (; scaledPixelNo < scaledWidth; scaledPixelNo++)
+ scalingX[scaledPixelNo] = pixelNo;
scaledWidth = MIN(clipRect.width(), scaledWidth);
scaledHeight = MIN(clipRect.height(), scaledHeight);
- offsetY = clipRect.top - rect.top;
- offsetX = clipRect.left - rect.left;
+ const int16 offsetY = clipRect.top - rect.top;
+ const int16 offsetX = clipRect.left - rect.left;
- for (y = 0; y < scaledHeight; y++) {
- for (x = 0; x < scaledWidth; x++) {
- color = bitmap[scalingY[y + offsetY] * celWidth + scalingX[x + offsetX]];
- if (color != clearKey && priority >= _screen->getPriority(clipRectTranslated.left + x, clipRectTranslated.top + y)) {
- _screen->putPixel(clipRectTranslated.left + x, clipRectTranslated.top + y, drawMask, palette->mapping[color], priority, 0);
+ // Happens in SQ6, first room
+ if (offsetX < 0 || offsetY < 0)
+ return;
+
+ assert(scaledHeight + offsetY <= ARRAYSIZE(scalingY));
+ assert(scaledWidth + offsetX <= ARRAYSIZE(scalingX));
+ for (int y = 0; y < scaledHeight; y++) {
+ for (int x = 0; x < scaledWidth; x++) {
+ const byte color = bitmap[scalingY[y + offsetY] * celWidth + scalingX[x + offsetX]];
+ const int x2 = clipRectTranslated.left + x;
+ const int y2 = clipRectTranslated.top + y;
+ if (color != clearKey && priority >= _screen->getPriority(x2, y2)) {
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
}
}
}
}
-uint16 GfxView::getCelCount(int16 loopNo) {
- if ((loopNo < 0) || (loopNo >= _loopCount))
- return 0;
- return _loop[loopNo].celCount;
-}
-
-Palette *GfxView::getPalette() {
- return _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
-}
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index a2050dc9d5..990a7e2f71 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -30,6 +30,7 @@ namespace Sci {
struct CelInfo {
int16 width, height;
+ int16 scriptWidth, scriptHeight;
int16 displaceX;
int16 displaceY;
byte clearKey;
@@ -60,39 +61,53 @@ public:
GfxView(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId);
~GfxView();
- GuiResourceId getResourceId();
- int16 getWidth(int16 loopNo, int16 celNo);
- int16 getHeight(int16 loopNo, int16 celNo);
- CelInfo *getCelInfo(int16 loopNo, int16 celNo);
- LoopInfo *getLoopInfo(int16 loopNo);
- void getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect *outRect);
- void getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect *outRect);
- byte *getBitmap(int16 loopNo, int16 celNo);
- void draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
- void drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY);
+ GuiResourceId getResourceId() const;
+ int16 getWidth(int16 loopNo, int16 celNo) const;
+ int16 getHeight(int16 loopNo, int16 celNo) const;
+ const CelInfo *getCelInfo(int16 loopNo, int16 celNo) const;
+ void getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
+ void getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const;
+ const byte *getBitmap(int16 loopNo, int16 celNo);
+ void draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
+ void drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY);
uint16 getLoopCount() const { return _loopCount; }
- uint16 getCelCount(int16 loopNo);
+ uint16 getCelCount(int16 loopNo) const;
Palette *getPalette();
+ bool isScaleable();
+ bool isSci2Hires();
+
private:
void initData(GuiResourceId resourceId);
void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount);
void unditherBitmap(byte *bitmap, int16 width, int16 height, byte clearKey);
ResourceManager *_resMan;
+ GfxCoordAdjuster *_coordAdjuster;
GfxScreen *_screen;
GfxPalette *_palette;
GuiResourceId _resourceId;
Resource *_resource;
byte *_resourceData;
+ int _resourceSize;
uint16 _loopCount;
LoopInfo *_loop;
bool _embeddedPal;
Palette _viewPalette;
+ // set for SCI2 views in gk1/windows, means that views are hires and should be handled accordingly
+ bool _isSci2Hires;
+
byte *_EGAmapping;
+
+ // this is set for sci0early to adjust for the getCelRect() change
+ int16 _adjustForSci0Early;
+
+ // this is not set for some views in laura bow 2 floppy and signals that the view shall never get scaled
+ // even if scaleX/Y are set (inside kAnimate)
+ bool _isScaleable;
};
} // End of namespace Sci
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 8c4d666ba7..238209c446 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -6,10 +6,10 @@ MODULE_OBJS := \
detection.o \
event.o \
resource.o \
+ resource_audio.o \
sci.o \
util.o \
engine/features.o \
- engine/game.o \
engine/gc.o \
engine/kernel.o \
engine/kevent.o \
@@ -25,16 +25,19 @@ MODULE_OBJS := \
engine/kscripts.o \
engine/ksound.o \
engine/kstring.o \
+ engine/kvideo.o \
engine/message.o \
engine/savegame.o \
engine/script.o \
engine/scriptdebug.o \
+ engine/script_patches.o \
engine/selector.o \
engine/seg_manager.o \
engine/segment.o \
engine/state.o \
engine/static_selectors.o \
engine/vm.o \
+ engine/workarounds.o \
graphics/animate.o \
graphics/cache.o \
graphics/compare.o \
@@ -43,7 +46,7 @@ MODULE_OBJS := \
graphics/cursor.o \
graphics/font.o \
graphics/fontsjis.o \
- graphics/gui.o \
+ graphics/maciconbar.o \
graphics/menu.o \
graphics/paint.o \
graphics/paint16.o \
@@ -63,24 +66,18 @@ MODULE_OBJS := \
sound/music.o \
sound/soundcmd.o \
sound/drivers/adlib.o \
- sound/drivers/amiga.o \
+ sound/drivers/amigamac.o \
sound/drivers/fb01.o \
sound/drivers/midi.o \
sound/drivers/pcjr.o \
- sound/iterator/core.o \
- sound/iterator/iterator.o \
- sound/iterator/songlib.o \
video/seq_decoder.o
ifdef ENABLE_SCI32
MODULE_OBJS += \
- engine/kernel32.o \
graphics/frameout.o \
- graphics/gui32.o \
graphics/paint32.o \
- graphics/robot.o \
- video/vmd_decoder.o
+ graphics/robot.o
endif
# This module can be built as a plugin
diff --git a/engines/sci/parser/grammar.cpp b/engines/sci/parser/grammar.cpp
index 1cfe84076f..6f37b49919 100644
--- a/engines/sci/parser/grammar.cpp
+++ b/engines/sci/parser/grammar.cpp
@@ -258,6 +258,11 @@ void Vocabulary::freeRuleList(ParseRuleList *list) {
static ParseRuleList *_vocab_add_rule(ParseRuleList *list, ParseRule *rule) {
if (!rule)
return list;
+ if (!rule->_data.size()) {
+ // Special case for qfg2 demo
+ warning("no rule contents on _vocab_add_rule()");
+ return list;
+ }
ParseRuleList *new_elem = new ParseRuleList(rule);
@@ -417,44 +422,44 @@ ParseRuleList *Vocabulary::buildGNF(bool verbose) {
return tlist;
}
-static int _vbpt_pareno(parse_tree_node_t *nodes, int *pos, int base) {
+static int _vbpt_pareno(ParseTreeNode *nodes, int *pos, int base) {
// Opens parentheses
- nodes[base].content.branches[0] = (*pos) + 1;
+ nodes[base].left = &nodes[(*pos) + 1];
nodes[++(*pos)].type = kParseTreeBranchNode;
- nodes[*pos].content.branches[0] = 0;
- nodes[*pos].content.branches[1] = 0;
+ nodes[*pos].left = 0;
+ nodes[*pos].right = 0;
return *pos;
}
-static int _vbpt_parenc(parse_tree_node_t *nodes, int *pos, int paren) {
+static int _vbpt_parenc(ParseTreeNode *nodes, int *pos, int paren) {
// Closes parentheses for appending
- nodes[paren].content.branches[1] = ++(*pos);
+ nodes[paren].right = &nodes[++(*pos)];
nodes[*pos].type = kParseTreeBranchNode;
- nodes[*pos].content.branches[0] = 0;
- nodes[*pos].content.branches[1] = 0;
+ nodes[*pos].left = 0;
+ nodes[*pos].right = 0;
return *pos;
}
-static int _vbpt_append(parse_tree_node_t *nodes, int *pos, int base, int value) {
+static int _vbpt_append(ParseTreeNode *nodes, int *pos, int base, int value) {
// writes one value to an existing base node and creates a successor node for writing
- nodes[base].content.branches[0] = ++(*pos);
+ nodes[base].left = &nodes[++(*pos)];
nodes[*pos].type = kParseTreeLeafNode;
- nodes[*pos].content.value = value;
- nodes[base].content.branches[1] = ++(*pos);
+ nodes[*pos].value = value;
+ nodes[base].right = &nodes[++(*pos)];
nodes[*pos].type = kParseTreeBranchNode;
- nodes[*pos].content.branches[0] = 0;
- nodes[*pos].content.branches[1] = 0;
+ nodes[*pos].left = 0;
+ nodes[*pos].right = 0;
return *pos;
}
-static int _vbpt_terminate(parse_tree_node_t *nodes, int *pos, int base, int value) {
+static int _vbpt_terminate(ParseTreeNode *nodes, int *pos, int base, int value) {
// Terminates, overwriting a nextwrite forknode
nodes[base].type = kParseTreeLeafNode;
- nodes[base].content.value = value;
+ nodes[base].value = value;
return *pos;
}
-static int _vbpt_write_subexpression(parse_tree_node_t *nodes, int *pos, ParseRule *rule, uint rulepos, int writepos) {
+static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule *rule, uint rulepos, int writepos) {
uint token;
while ((token = ((rulepos < rule->_data.size()) ? rule->_data[rulepos++] : TOKEN_CPAREN)) != TOKEN_CPAREN) {
@@ -560,15 +565,15 @@ int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) {
int temp, pos;
_parserNodes[0].type = kParseTreeBranchNode;
- _parserNodes[0].content.branches[0] = 1;
- _parserNodes[0].content.branches[1] = 2;
+ _parserNodes[0].left = &_parserNodes[1];
+ _parserNodes[0].right = &_parserNodes[2];
_parserNodes[1].type = kParseTreeLeafNode;
- _parserNodes[1].content.value = 0x141;
+ _parserNodes[1].value = 0x141;
_parserNodes[2].type = kParseTreeBranchNode;
- _parserNodes[2].content.branches[0] = 0;
- _parserNodes[2].content.branches[1] = 0;
+ _parserNodes[2].left = 0;
+ _parserNodes[2].right = 0;
pos = 2;
diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp
index 5cd1310ad3..9c07be2dff 100644
--- a/engines/sci/parser/said.cpp
+++ b/engines/sci/parser/said.cpp
@@ -1,111 +1,3 @@
-/* A Bison parser, made by GNU Bison 2.3. */
-
-/* Skeleton implementation for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
-
-/* As a special exception, you may create a larger work that contains
- part or all of the Bison parser skeleton and distribute that work
- under terms of your choice, so long as that work isn't itself a
- parser generator using the skeleton or a modified version thereof
- as a parser skeleton. Alternatively, if you modify or redistribute
- the parser skeleton itself, you may (at your option) remove this
- special exception, which will cause the skeleton and the resulting
- Bison output files to be licensed under the GNU General Public
- License without this special exception.
-
- This special exception was added by the Free Software Foundation in
- version 2.2 of Bison. */
-
-/* C LALR(1) parser skeleton written by Richard Stallman, by
- simplifying the original so-called "semantic" parser. */
-
-/* All symbols defined below should begin with yy or YY, to avoid
- infringing on user name space. This should be done even for local
- variables, as they might otherwise be expanded by user macros.
- There are some unavoidable exceptions within include files to
- define necessary library symbols; they are noted "INFRINGES ON
- USER NAME SPACE" below. */
-
-/* Identify Bison output. */
-#define YYBISON 1
-
-/* Bison version. */
-#define YYBISON_VERSION "2.3"
-
-/* Skeleton name. */
-#define YYSKELETON_NAME "yacc.c"
-
-/* Pure parsers. */
-#define YYPURE 0
-
-/* Using locations. */
-#define YYLSP_NEEDED 0
-
-
-
-/* Tokens. */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
- /* Put the tokens into the symbol table, so that GDB and other debuggers
- know about them. */
- enum yytokentype {
- WGROUP = 258,
- YY_COMMA = 259,
- YY_AMP = 260,
- YY_SLASH = 261,
- YY_PARENO = 262,
- YY_PARENC = 263,
- YY_BRACKETSO = 264,
- YY_BRACKETSC = 265,
- YY_HASH = 266,
- YY_LT = 267,
- YY_GT = 268,
- YY_BRACKETSO_LT = 269,
- YY_BRACKETSO_SLASH = 270,
- YY_LT_BRACKETSO = 271,
- YY_LT_PARENO = 272
- };
-#endif
-/* Tokens. */
-#define WGROUP 258
-#define YY_COMMA 259
-#define YY_AMP 260
-#define YY_SLASH 261
-#define YY_PARENO 262
-#define YY_PARENC 263
-#define YY_BRACKETSO 264
-#define YY_BRACKETSC 265
-#define YY_HASH 266
-#define YY_LT 267
-#define YY_GT 268
-#define YY_BRACKETSO_LT 269
-#define YY_BRACKETSO_SLASH 270
-#define YY_LT_BRACKETSO 271
-#define YY_LT_PARENO 272
-
-
-
-
-/* Copy the first part of user declarations. */
-
-
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
@@ -133,14 +25,6 @@
#include "sci/engine/state.h"
-
-// Bison generates an empty switch statement that gives a warning in MSVC.
-// This disables that warning.
-#ifdef _MSC_VER
-#pragma warning(disable:4065)
-#endif
-
-
namespace Sci {
#define SAID_BRANCH_NULL 0
@@ -150,25 +34,8 @@ namespace Sci {
// Maximum number of words to be expected in a parsed sentence
#define AUGMENT_MAX_WORDS 64
-
-#define ANYWORD 0xfff
-
-#define WORD_TYPE_BASE 0x141
-#define WORD_TYPE_REF 0x144
-#define WORD_TYPE_SYNTACTIC_SUGAR 0x145
-
-#define AUGMENT_SENTENCE_PART_BRACKETS 0x152
-
-// Minor numbers
-#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c
-#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153
-#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144
-#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f
-
-
-#undef YYDEBUG /*1*/
-//#define SAID_DEBUG*/
-//#define SCI_DEBUG_PARSE_TREE_AUGMENTATION // uncomment to debug parse tree augmentation
+// uncomment to debug parse tree augmentation
+//#define SCI_DEBUG_PARSE_TREE_AUGMENTATION
#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
@@ -179,2305 +46,1084 @@ void print_nothing(...) { }
#endif
-static char *said_parse_error;
-
static int said_token;
static int said_tokens_nr;
static int said_tokens[MAX_SAID_TOKENS];
-static int said_blessed; // increminated by said_top_branch
-static int said_tree_pos; // Set to 0 if we're out of space
-#define SAID_TREE_START 4; // Reserve space for the 4 top nodes
+static int said_tree_pos;
+#define SAID_TREE_START 4 // Reserve space for the 4 top nodes
+
+enum SaidToken {
+ TOKEN_COMMA = 0xF000,
+ TOKEN_AMP = 0xF100,
+ TOKEN_SLASH = 0xF200,
+ TOKEN_PARENO = 0xF300,
+ TOKEN_PARENC = 0xF400,
+ TOKEN_BRACKETO = 0xF500,
+ TOKEN_BRACKETC = 0xF600,
+ TOKEN_HASH = 0xF700,
+ TOKEN_LT = 0xF800,
+ TOKEN_GT = 0xF900,
+ TOKEN_TERM = 0xFF00
+};
-#define VALUE_IGNORE -424242
+enum SaidWord {
+ WORD_NONE = 0x0ffe,
+ WORD_ANY = 0x0fff
+};
-static parse_tree_node_t said_tree[VOCAB_TREE_NODES];
-typedef int wgroup_t;
-typedef int tree_t;
-typedef int said_spec_t;
-static tree_t said_aug_branch(int, int, tree_t, tree_t);
-static tree_t said_attach_branch(tree_t, tree_t);
-/*
-static tree_t said_wgroup_branch(wgroup_t);
-*/
-static said_spec_t said_top_branch(tree_t);
-static tree_t said_paren(tree_t, tree_t);
-static tree_t said_value(int, tree_t);
-static tree_t said_terminal(int);
+// TODO: maybe turn this into a proper n-ary tree instead of an
+// n-ary tree implemented in terms of a binary tree.
+// (Together with _parserNodes in Vocabulary)
-static int yylex();
+static ParseTreeNode said_tree[VOCAB_TREE_NODES];
-static int yyerror(const char *s) {
- said_parse_error = strdup(s);
- return 1; /* Abort */
-}
+typedef int wgroup_t;
+typedef int said_spec_t;
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
+static ParseTreeNode* said_next_node() {
+ assert(said_tree_pos > 0 && said_tree_pos < VOCAB_TREE_NODES);
-/* Enabling verbose error messages. */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
+ return &said_tree[said_tree_pos++];
+}
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
+static ParseTreeNode* said_leaf_node(ParseTreeNode* pos, int value) {
+ pos->type = kParseTreeLeafNode;
+ pos->value = value;
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-typedef int YYSTYPE;
-# define yystype YYSTYPE /* obsolescent; will be withdrawn */
-# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
-#endif
+ return pos;
+}
+static ParseTreeNode* said_word_node(ParseTreeNode* pos, int value) {
+ pos->type = kParseTreeWordNode;
+ pos->value = value;
+ return pos;
+}
-/* Copy the second part of user declarations. */
+static ParseTreeNode* said_branch_node(ParseTreeNode* pos,
+ ParseTreeNode* left,
+ ParseTreeNode* right) {
+ pos->type = kParseTreeBranchNode;
+ pos->left = left;
+ pos->right = right;
+ return pos;
+}
-/* Line 216 of yacc.c. */
+static ParseTreeNode* said_branch_attach_left(ParseTreeNode* pos,
+ ParseTreeNode* left) {
+ pos->type = kParseTreeBranchNode;
+ pos->left = left;
+ return pos;
-#ifdef short
-# undef short
-#endif
+}
-#ifdef YYTYPE_UINT8
-typedef YYTYPE_UINT8 yytype_uint8;
-#else
-typedef unsigned char yytype_uint8;
-#endif
+static ParseTreeNode* said_branch_attach_right(ParseTreeNode* pos,
+ ParseTreeNode* right) {
+ pos->type = kParseTreeBranchNode;
+ pos->right = right;
-#ifdef YYTYPE_INT8
-typedef YYTYPE_INT8 yytype_int8;
-#elif (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-typedef signed char yytype_int8;
-#else
-typedef short int yytype_int8;
-#endif
+ return pos;
+}
-#ifdef YYTYPE_UINT16
-typedef YYTYPE_UINT16 yytype_uint16;
-#else
-typedef unsigned short int yytype_uint16;
-#endif
-#ifdef YYTYPE_INT16
-typedef YYTYPE_INT16 yytype_int16;
-#else
-typedef short int yytype_int16;
-#endif
+/*
+ pos
+ / \
+ . \
+ *
+ / \
+ / 0
+ *
+ / \
+ / \
+ / subtree
+ major / \
+ / .
+ minor
+
+ . = unchanged child node
+ * = new branch node
+ 0 = NULL child node. (Location for future siblings of the subtree)
-#ifndef YYSIZE_T
-# ifdef __SIZE_TYPE__
-# define YYSIZE_T __SIZE_TYPE__
-# elif defined size_t
-# define YYSIZE_T size_t
-# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
-# define YYSIZE_T size_t
-# else
-# define YYSIZE_T unsigned int
-# endif
-#endif
+*/
-#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
-
-#ifndef YY_
-# if YYENABLE_NLS
-# if ENABLE_NLS
-# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
-# define YY_(msgid) dgettext ("bison-runtime", msgid)
-# endif
-# endif
-# ifndef YY_
-# define YY_(msgid) msgid
-# endif
-#endif
+static bool said_attach_subtree(ParseTreeNode* pos, int major, int minor,
+ ParseTreeNode* subtree) {
+ bool retval = true;
-/* Suppress unused-variable warnings by "using" E. */
-#if ! defined lint || defined __GNUC__
-# define YYUSE(e) ((void) (e))
-#else
-# define YYUSE(e) /* empty */
-#endif
+ said_branch_attach_right(pos,
+ said_branch_node(said_next_node(),
+ said_branch_node(said_next_node(),
+ said_leaf_node(said_next_node(), major),
+ said_branch_attach_left(subtree,
+ said_leaf_node(said_next_node(), minor))),
+ 0));
-/* Identity function, used to suppress warnings about constant conditions. */
-#ifndef lint
-# define YYID(n) (n)
-#else
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static int
-YYID (int i)
-#else
-static int
-YYID (i)
- int i;
-#endif
-{
- return i;
+ return retval;
}
-#endif
-#if ! defined yyoverflow || YYERROR_VERBOSE
-
-/* The parser invokes alloca or malloc; define the necessary symbols. */
-
-# ifdef YYSTACK_USE_ALLOCA
-# if YYSTACK_USE_ALLOCA
-# ifdef __GNUC__
-# define YYSTACK_ALLOC __builtin_alloca
-# elif defined __BUILTIN_VA_ARG_INCR
-# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
-# elif defined _AIX
-# define YYSTACK_ALLOC __alloca
-# elif defined _MSC_VER
-# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
-# define alloca _alloca
-# else
-# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
-# endif
-# endif
-# endif
-# endif
-# endif
-
-# ifdef YYSTACK_ALLOC
- /* Pacify GCC's `empty if-body' warning. */
-# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
-# ifndef YYSTACK_ALLOC_MAXIMUM
- /* The OS might guarantee only one guard page at the bottom of the stack,
- and a page size can be as small as 4096 bytes. So we cannot safely
- invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
- to allow for a few compiler-allocated temporary stack slots. */
-# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
-# endif
-# else
-# define YYSTACK_ALLOC YYMALLOC
-# define YYSTACK_FREE YYFREE
-# ifndef YYSTACK_ALLOC_MAXIMUM
-# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
-# endif
-# if (defined __cplusplus && ! defined _STDLIB_H \
- && ! ((defined YYMALLOC || defined malloc) \
- && (defined YYFREE || defined free)))
-# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
-# endif
-# endif
-# ifndef YYMALLOC
-# define YYMALLOC malloc
-# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
-# endif
-# endif
-# ifndef YYFREE
-# define YYFREE free
-# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-void free (void *); /* INFRINGES ON USER NAME SPACE */
-# endif
-# endif
-# endif
-#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
-
-
-#if (! defined yyoverflow \
- && (! defined __cplusplus \
- || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
-
-/* A type that is properly aligned for any stack member. */
-union yyalloc
-{
- yytype_int16 yyss;
- YYSTYPE yyvs;
- };
-
-/* The size of the maximum gap between one aligned stack and the next. */
-# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
-
-/* The size of an array large to enough to hold all stacks, each with
- N elements. */
-# define YYSTACK_BYTES(N) \
- ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
- + YYSTACK_GAP_MAXIMUM)
-
-/* Copy COUNT objects from FROM to TO. The source and destination do
- not overlap. */
-# ifndef YYCOPY
-# if defined __GNUC__ && 1 < __GNUC__
-# define YYCOPY(To, From, Count) \
- __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
-# else
-# define YYCOPY(To, From, Count) \
- do \
- { \
- YYSIZE_T yyi; \
- for (yyi = 0; yyi < (Count); yyi++) \
- (To)[yyi] = (From)[yyi]; \
- } \
- while (YYID (0))
-# endif
-# endif
-
-/* Relocate STACK from its old location to the new one. The
- local variables YYSIZE and YYSTACKSIZE give the old and new number of
- elements in the stack, and YYPTR gives the new location of the
- stack. Advance YYPTR to a properly aligned location for the next
- stack. */
-# define YYSTACK_RELOCATE(Stack) \
- do \
- { \
- YYSIZE_T yynewbytes; \
- YYCOPY (&yyptr->Stack, Stack, yysize); \
- Stack = &yyptr->Stack; \
- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
- yyptr += yynewbytes / sizeof (*yyptr); \
- } \
- while (YYID (0))
-#endif
-/* YYFINAL -- State number of the termination state. */
-#define YYFINAL 23
-/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 80
-
-/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 18
-/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 13
-/* YYNRULES -- Number of rules. */
-#define YYNRULES 35
-/* YYNRULES -- Number of states. */
-#define YYNSTATES 69
-
-/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
-#define YYUNDEFTOK 2
-#define YYMAXUTOK 272
-
-#define YYTRANSLATE(YYX) \
- ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
-
-/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
-static const yytype_uint8 yytranslate[] =
-{
- 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
- 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17
-};
-#if YYDEBUG
-/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
- YYRHS. */
-static const yytype_uint8 yyprhs[] =
-{
- 0, 0, 3, 6, 10, 15, 16, 18, 19, 21,
- 24, 29, 31, 34, 39, 41, 43, 45, 49, 51,
- 55, 59, 64, 70, 73, 75, 77, 79, 83, 88,
- 92, 97, 100, 105, 109, 112
-};
+/*****************/
+/**** Parsing ****/
+/*****************/
-/* YYRHS -- A `-1'-separated list of the rules' RHS. */
-static const yytype_int8 yyrhs[] =
-{
- 19, 0, -1, 21, 20, -1, 21, 22, 20, -1,
- 21, 22, 23, 20, -1, -1, 13, -1, -1, 27,
- -1, 6, 27, -1, 15, 6, 27, 10, -1, 6,
- -1, 6, 27, -1, 15, 6, 27, 10, -1, 6,
- -1, 3, -1, 26, -1, 9, 26, 10, -1, 24,
- -1, 7, 27, 8, -1, 26, 4, 26, -1, 26,
- 14, 29, 10, -1, 26, 4, 9, 26, 10, -1,
- 25, 28, -1, 25, -1, 28, -1, 29, -1, 14,
- 29, 10, -1, 29, 14, 29, 10, -1, 12, 24,
- 30, -1, 17, 7, 27, 8, -1, 12, 26, -1,
- 16, 9, 26, 10, -1, 12, 26, 30, -1, 12,
- 26, -1, 17, 7, 27, 8, -1
-};
+static bool parseSpec(ParseTreeNode* parentNode);
+static bool parsePart2(ParseTreeNode* parentNode, bool& nonempty);
+static bool parsePart3(ParseTreeNode* parentNode, bool& nonempty);
+static bool parseSlash(ParseTreeNode* parentNode);
+static bool parseExpr(ParseTreeNode* parentNode);
+static bool parseRef(ParseTreeNode* parentNode);
+static bool parseComma(ParseTreeNode* parentNode);
+static bool parseList(ParseTreeNode* parentNode);
+static bool parseListEntry(ParseTreeNode* parentNode);
+static bool parseWord(ParseTreeNode* parentNode);
-/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
-static const yytype_uint8 yyrline[] =
+static bool parseWord(ParseTreeNode* parentNode)
{
- 0, 130, 130, 132, 134, 140, 141, 148, 149, 155,
- 157, 159, 165, 167, 169, 174, 179, 181, 186, 188,
- 190, 192, 194, 199, 201, 203, 208, 210, 212, 217,
- 219, 221, 223, 228, 230, 232
-};
-#endif
+ int token = said_tokens[said_token];
+ if (token & 0x8000)
+ return false;
-#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
-/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
- First, the terminals, then, starting at YYNTOKENS, nonterminals. */
-static const char *const yytname[] =
-{
- "$end", "error", "$undefined", "WGROUP", "YY_COMMA", "YY_AMP",
- "YY_SLASH", "YY_PARENO", "YY_PARENC", "YY_BRACKETSO", "YY_BRACKETSC",
- "YY_HASH", "YY_LT", "YY_GT", "YY_BRACKETSO_LT", "YY_BRACKETSO_SLASH",
- "YY_LT_BRACKETSO", "YY_LT_PARENO", "$accept", "saidspec", "optcont",
- "leftspec", "midspec", "rightspec", "word", "cwordset", "wordset",
- "expr", "cwordrefset", "wordrefset", "recref", 0
-};
-#endif
+ said_token++;
-# ifdef YYPRINT
-/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
- token YYLEX-NUM. */
-static const yytype_uint16 yytoknum[] =
-{
- 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
- 265, 266, 267, 268, 269, 270, 271, 272
-};
-# endif
+ ParseTreeNode* newNode = said_word_node(said_next_node(), token);
-/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
-static const yytype_uint8 yyr1[] =
-{
- 0, 18, 19, 19, 19, 20, 20, 21, 21, 22,
- 22, 22, 23, 23, 23, 24, 25, 25, 26, 26,
- 26, 26, 26, 27, 27, 27, 28, 28, 28, 29,
- 29, 29, 29, 30, 30, 30
-};
+ parentNode->right = newNode;
-/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
-static const yytype_uint8 yyr2[] =
-{
- 0, 2, 2, 3, 4, 0, 1, 0, 1, 2,
- 4, 1, 2, 4, 1, 1, 1, 3, 1, 3,
- 3, 4, 5, 2, 1, 1, 1, 3, 4, 3,
- 4, 2, 4, 3, 2, 4
-};
+ return true;
+}
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
- STATE-NUM when YYTABLE doesn't specify something else to do. Zero
- means the default is an error. */
-static const yytype_uint8 yydefact[] =
+static bool parsePart2(ParseTreeNode* parentNode, bool& nonempty)
{
- 7, 15, 0, 0, 0, 0, 0, 0, 0, 5,
- 18, 24, 16, 8, 25, 26, 0, 0, 18, 31,
- 0, 0, 0, 1, 11, 6, 0, 2, 5, 23,
- 0, 0, 0, 19, 17, 0, 0, 29, 27, 0,
- 0, 9, 0, 14, 0, 3, 5, 0, 20, 0,
- 0, 34, 0, 32, 30, 0, 12, 0, 4, 0,
- 21, 28, 33, 0, 10, 0, 22, 35, 13
-};
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
-/* YYDEFGOTO[NTERM-NUM]. */
-static const yytype_int8 yydefgoto[] =
-{
- -1, 8, 27, 9, 28, 46, 10, 11, 12, 13,
- 14, 15, 37
-};
+ ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0);
-/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
- STATE-NUM. */
-#define YYPACT_NINF -24
-static const yytype_int8 yypact[] =
-{
- -1, -24, -1, 62, 62, 54, 1, 5, 18, 38,
- -24, 47, 3, -24, -24, 12, 23, 15, -3, 3,
- 28, 62, -1, -24, -1, -24, 42, -24, 39, -24,
- 53, 54, 54, -24, -24, 62, 50, -24, -24, 29,
- 41, -24, -1, -1, 52, -24, 55, 62, 3, 57,
- 63, 20, -1, -24, -24, 64, -24, -1, -24, 32,
- -24, -24, -24, 67, -24, 66, -24, -24, -24
-};
+ nonempty = true;
-/* YYPGOTO[NTERM-NUM]. */
-static const yytype_int8 yypgoto[] =
-{
- -24, -24, -23, -24, -24, -24, 68, -24, 0, -2,
- 69, -4, 26
-};
+ bool found;
-/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
- positive, shift that token. If negative, reduce the rule which
- number is the opposite. If zero, do what YYDEFACT says.
- If YYTABLE_NINF, syntax error. */
-#define YYTABLE_NINF -1
-static const yytype_uint8 yytable[] =
-{
- 16, 20, 1, 17, 19, 45, 2, 30, 3, 35,
- 21, 4, 22, 5, 36, 6, 7, 31, 23, 30,
- 40, 39, 41, 58, 30, 34, 32, 49, 50, 31,
- 48, 33, 35, 30, 31, 51, 30, 36, 38, 53,
- 55, 56, 66, 31, 24, 43, 31, 59, 42, 54,
- 63, 25, 25, 26, 44, 65, 1, 52, 57, 4,
- 2, 5, 47, 6, 7, 1, 4, 60, 25, 2,
- 6, 7, 18, 61, 64, 67, 68, 62, 0, 0,
- 29
-};
+ found = parseSlash(newNode);
-static const yytype_int8 yycheck[] =
-{
- 2, 5, 3, 3, 4, 28, 7, 4, 9, 12,
- 9, 12, 7, 14, 17, 16, 17, 14, 0, 4,
- 22, 21, 24, 46, 4, 10, 14, 31, 32, 14,
- 30, 8, 12, 4, 14, 35, 4, 17, 10, 10,
- 42, 43, 10, 14, 6, 6, 14, 47, 6, 8,
- 52, 13, 13, 15, 15, 57, 3, 7, 6, 12,
- 7, 14, 9, 16, 17, 3, 12, 10, 13, 7,
- 16, 17, 4, 10, 10, 8, 10, 51, -1, -1,
- 11
-};
+ if (found) {
-/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
- symbol of state STATE-NUM. */
-static const yytype_uint8 yystos[] =
-{
- 0, 3, 7, 9, 12, 14, 16, 17, 19, 21,
- 24, 25, 26, 27, 28, 29, 27, 26, 24, 26,
- 29, 9, 7, 0, 6, 13, 15, 20, 22, 28,
- 4, 14, 14, 8, 10, 12, 17, 30, 10, 26,
- 27, 27, 6, 6, 15, 20, 23, 9, 26, 29,
- 29, 26, 7, 10, 8, 27, 27, 6, 20, 26,
- 10, 10, 30, 27, 10, 27, 10, 8, 10
-};
-
-#define yyerrok (yyerrstatus = 0)
-#define yyclearin (yychar = YYEMPTY)
-#define YYEMPTY (-2)
-#define YYEOF 0
-
-#define YYACCEPT goto yyacceptlab
-#define YYABORT goto yyabortlab
-#define YYERROR goto yyerrorlab
-
-
-/* Like YYERROR except do call yyerror. This remains here temporarily
- to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. */
-
-#define YYFAIL goto yyerrlab
-
-#define YYRECOVERING() (!!yyerrstatus)
-
-#define YYBACKUP(Token, Value) \
-do \
- if (yychar == YYEMPTY && yylen == 1) \
- { \
- yychar = (Token); \
- yylval = (Value); \
- yytoken = YYTRANSLATE (yychar); \
- YYPOPSTACK (1); \
- goto yybackup; \
- } \
- else \
- { \
- yyerror (YY_("syntax error: cannot back up")); \
- YYERROR; \
- } \
-while (YYID (0))
-
-
-#define YYTERROR 1
-#define YYERRCODE 256
-
-
-/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
- If N is 0, then set CURRENT to the empty location which ends
- the previous symbol: RHS[0] (always defined). */
-
-#define YYRHSLOC(Rhs, K) ((Rhs)[K])
-#ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N) \
- do \
- if (YYID (N)) \
- { \
- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
- } \
- else \
- { \
- (Current).first_line = (Current).last_line = \
- YYRHSLOC (Rhs, 0).last_line; \
- (Current).first_column = (Current).last_column = \
- YYRHSLOC (Rhs, 0).last_column; \
- } \
- while (YYID (0))
-#endif
+ said_attach_subtree(parentNode, 0x142, 0x14a, newNode);
+ return true;
-/* YY_LOCATION_PRINT -- Print the location on the stream.
- This macro was not mandated originally: define only if we know
- we won't break user code: when these are the locations we know. */
+ } else if (said_tokens[said_token] == TOKEN_BRACKETO) {
+ said_token++;
+
+ found = parsePart2(newNode, nonempty);
-#ifndef YY_LOCATION_PRINT
-# if YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- fprintf (File, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
-#endif
+ if (found) {
+ if (said_tokens[said_token] == TOKEN_BRACKETC) {
+ said_token++;
-/* YYLEX -- calling `yylex' with the right arguments. */
+ said_attach_subtree(parentNode, 0x152, 0x142, newNode);
-#ifdef YYLEX_PARAM
-# define YYLEX yylex (YYLEX_PARAM)
-#else
-# define YYLEX yylex ()
-#endif
+ return true;
+ }
+ }
-/* Enable debugging if requested. */
-#if YYDEBUG
-
-# ifndef YYFPRINTF
-# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
-# define YYFPRINTF fprintf
-# endif
-
-# define YYDPRINTF(Args) \
-do { \
- if (yydebug) \
- YYFPRINTF Args; \
-} while (YYID (0))
-
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
-do { \
- if (yydebug) \
- { \
- YYFPRINTF (stderr, "%s ", Title); \
- yy_symbol_print (stderr, \
- Type, Value); \
- YYFPRINTF (stderr, "\n"); \
- } \
-} while (YYID (0))
-
-
-/*--------------------------------.
-| Print this symbol on YYOUTPUT. |
-`--------------------------------*/
-
-/*ARGSUSED*/
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
-#else
-static void
-yy_symbol_value_print (yyoutput, yytype, yyvaluep)
- FILE *yyoutput;
- int yytype;
- YYSTYPE const * const yyvaluep;
-#endif
-{
- if (!yyvaluep)
- return;
-# ifdef YYPRINT
- if (yytype < YYNTOKENS)
- YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
-# else
- YYUSE (yyoutput);
-# endif
- switch (yytype)
- {
- default:
- break;
- }
-}
+ }
+ // CHECKME: this doesn't look right if the [] section matched partially
+ // Should the below 'if' be an 'else if' ?
-/*--------------------------------.
-| Print this symbol on YYOUTPUT. |
-`--------------------------------*/
+ if (said_tokens[said_token] == TOKEN_SLASH) {
+ said_token++;
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
-#else
-static void
-yy_symbol_print (yyoutput, yytype, yyvaluep)
- FILE *yyoutput;
- int yytype;
- YYSTYPE const * const yyvaluep;
-#endif
-{
- if (yytype < YYNTOKENS)
- YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
- else
- YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+ nonempty = false;
- yy_symbol_value_print (yyoutput, yytype, yyvaluep);
- YYFPRINTF (yyoutput, ")");
-}
+ return true;
-/*------------------------------------------------------------------.
-| yy_stack_print -- Print the state stack from its BOTTOM up to its |
-| TOP (included). |
-`------------------------------------------------------------------*/
+ }
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
-#else
-static void
-yy_stack_print (bottom, top)
- yytype_int16 *bottom;
- yytype_int16 *top;
-#endif
-{
- YYFPRINTF (stderr, "Stack now");
- for (; bottom <= top; ++bottom)
- YYFPRINTF (stderr, " %d", *bottom);
- YYFPRINTF (stderr, "\n");
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
}
-# define YY_STACK_PRINT(Bottom, Top) \
-do { \
- if (yydebug) \
- yy_stack_print ((Bottom), (Top)); \
-} while (YYID (0))
+static bool parsePart3(ParseTreeNode* parentNode, bool& nonempty)
+{
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
+ ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0);
-/*------------------------------------------------.
-| Report that the YYRULE is going to be reduced. |
-`------------------------------------------------*/
+ bool found;
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
-#else
-static void
-yy_reduce_print (yyvsp, yyrule)
- YYSTYPE *yyvsp;
- int yyrule;
-#endif
-{
- int yynrhs = yyr2[yyrule];
- int yyi;
- unsigned long int yylno = yyrline[yyrule];
- YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
- yyrule - 1, yylno);
- /* The symbols being reduced. */
- for (yyi = 0; yyi < yynrhs; yyi++)
- {
- fprintf (stderr, " $%d = ", yyi + 1);
- yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
- &(yyvsp[(yyi + 1) - (yynrhs)])
- );
- fprintf (stderr, "\n");
- }
-}
+ nonempty = true;
-# define YY_REDUCE_PRINT(Rule) \
-do { \
- if (yydebug) \
- yy_reduce_print (yyvsp, Rule); \
-} while (YYID (0))
-
-/* Nonzero means print parse trace. It is left uninitialized so that
- multiple parsers can coexist. */
-int yydebug;
-#else /* !YYDEBUG */
-# define YYDPRINTF(Args)
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
-# define YY_STACK_PRINT(Bottom, Top)
-# define YY_REDUCE_PRINT(Rule)
-#endif /* !YYDEBUG */
-
-
-/* YYINITDEPTH -- initial size of the parser's stacks. */
-#ifndef YYINITDEPTH
-# define YYINITDEPTH 200
-#endif
+ found = parseSlash(newNode);
-/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
- if the built-in stack extension method is used).
+ if (found) {
- Do not make this value too large; the results are undefined if
- YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
- evaluated with infinite-precision integer arithmetic. */
+ said_attach_subtree(parentNode, 0x143, 0x14a, newNode);
-#ifndef YYMAXDEPTH
-# define YYMAXDEPTH 10000
-#endif
+ return true;
-
+ } else if (said_tokens[said_token] == TOKEN_BRACKETO) {
+ said_token++;
+
+ found = parsePart3(newNode, nonempty);
-#if YYERROR_VERBOSE
+ if (found) {
-# ifndef yystrlen
-# if defined __GLIBC__ && defined _STRING_H
-# define yystrlen strlen
-# else
-/* Return the length of YYSTR. */
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static YYSIZE_T
-yystrlen (const char *yystr)
-#else
-static YYSIZE_T
-yystrlen (yystr)
- const char *yystr;
-#endif
-{
- YYSIZE_T yylen;
- for (yylen = 0; yystr[yylen]; yylen++)
- continue;
- return yylen;
-}
-# endif
-# endif
-
-# ifndef yystpcpy
-# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
-# define yystpcpy stpcpy
-# else
-/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
- YYDEST. */
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static char *
-yystpcpy (char *yydest, const char *yysrc)
-#else
-static char *
-yystpcpy (yydest, yysrc)
- char *yydest;
- const char *yysrc;
-#endif
-{
- char *yyd = yydest;
- const char *yys = yysrc;
+ if (said_tokens[said_token] == TOKEN_BRACKETC) {
+ said_token++;
- while ((*yyd++ = *yys++) != '\0')
- continue;
+ said_attach_subtree(parentNode, 0x152, 0x143, newNode);
- return yyd - 1;
-}
-# endif
-# endif
-
-# ifndef yytnamerr
-/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
- quotes and backslashes, so that it's suitable for yyerror. The
- heuristic is that double-quoting is unnecessary unless the string
- contains an apostrophe, a comma, or backslash (other than
- backslash-backslash). YYSTR is taken from yytname. If YYRES is
- null, do not copy; instead, return the length of what the result
- would have been. */
-static YYSIZE_T
-yytnamerr (char *yyres, const char *yystr)
-{
- if (*yystr == '"')
- {
- YYSIZE_T yyn = 0;
- char const *yyp = yystr;
-
- for (;;)
- switch (*++yyp)
- {
- case '\'':
- case ',':
- goto do_not_strip_quotes;
-
- case '\\':
- if (*++yyp != '\\')
- goto do_not_strip_quotes;
- /* Fall through. */
- default:
- if (yyres)
- yyres[yyn] = *yyp;
- yyn++;
- break;
-
- case '"':
- if (yyres)
- yyres[yyn] = '\0';
- return yyn;
- }
- do_not_strip_quotes: ;
- }
-
- if (! yyres)
- return yystrlen (yystr);
-
- return yystpcpy (yyres, yystr) - yyres;
-}
-# endif
-
-/* Copy into YYRESULT an error message about the unexpected token
- YYCHAR while in state YYSTATE. Return the number of bytes copied,
- including the terminating null byte. If YYRESULT is null, do not
- copy anything; just return the number of bytes that would be
- copied. As a special case, return 0 if an ordinary "syntax error"
- message will do. Return YYSIZE_MAXIMUM if overflow occurs during
- size calculation. */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
- int yyn = yypact[yystate];
-
- if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
- return 0;
- else
- {
- int yytype = YYTRANSLATE (yychar);
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- int yysize_overflow = 0;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- int yyx;
-
-# if 0
- /* This is so xgettext sees the translatable formats that are
- constructed on the fly. */
- YY_("syntax error, unexpected %s");
- YY_("syntax error, unexpected %s, expecting %s");
- YY_("syntax error, unexpected %s, expecting %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
- char *yyfmt;
- char const *yyf;
- static char const yyunexpected[] = "syntax error, unexpected %s";
- static char const yyexpecting[] = ", expecting %s";
- static char const yyor[] = " or %s";
- char yyformat[sizeof yyunexpected
- + sizeof yyexpecting - 1
- + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
- * (sizeof yyor - 1))];
- char const *yyprefix = yyexpecting;
-
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 1;
-
- yyarg[0] = yytname[yytype];
- yyfmt = yystpcpy (yyformat, yyunexpected);
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- yyformat[sizeof yyunexpected - 1] = '\0';
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
- yyfmt = yystpcpy (yyfmt, yyprefix);
- yyprefix = yyor;
- }
-
- yyf = YY_(yyformat);
- yysize1 = yysize + yystrlen (yyf);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
-
- if (yysize_overflow)
- return YYSIZE_MAXIMUM;
-
- if (yyresult)
- {
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- char *yyp = yyresult;
- int yyi = 0;
- while ((*yyp = *yyf) != '\0')
- {
- if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyf += 2;
- }
- else
- {
- yyp++;
- yyf++;
+ return true;
+ }
}
- }
+
}
- return yysize;
- }
-}
-#endif /* YYERROR_VERBOSE */
-
-
-/*-----------------------------------------------.
-| Release the memory associated to this symbol. |
-`-----------------------------------------------*/
-
-/*ARGSUSED*/
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
-#else
-static void
-yydestruct (yymsg, yytype, yyvaluep)
- const char *yymsg;
- int yytype;
- YYSTYPE *yyvaluep;
-#endif
-{
- YYUSE (yyvaluep);
- if (!yymsg)
- yymsg = "Deleting";
- YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+ // CHECKME: this doesn't look right if the [] section matched partially
+ // Should the below 'if' be an 'else if' ?
- switch (yytype)
- {
+ if (said_tokens[said_token] == TOKEN_SLASH) {
+ said_token++;
- default:
- break;
- }
-}
-
+ nonempty = false;
-/* Prevent warnings from -Wmissing-prototypes. */
+ return true;
-#ifdef YYPARSE_PARAM
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void *YYPARSE_PARAM);
-#else
-int yyparse ();
-#endif
-#else /* ! YYPARSE_PARAM */
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void);
-#else
-int yyparse ();
-#endif
-#endif /* ! YYPARSE_PARAM */
+ }
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
-/* The look-ahead symbol. */
-int yychar;
+static bool parseSlash(ParseTreeNode* parentNode)
+{
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
-/* The semantic value of the look-ahead symbol. */
-YYSTYPE yylval;
+ if (said_tokens[said_token] == TOKEN_SLASH) {
+ said_token++;
-/* Number of syntax errors so far. */
-int yynerrs;
+ bool found = parseExpr(parentNode);
+ if (found)
+ return true;
+ }
-/*----------.
-| yyparse. |
-`----------*/
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
-#ifdef YYPARSE_PARAM
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-int
-yyparse (void *YYPARSE_PARAM)
-#else
-int
-yyparse (YYPARSE_PARAM)
- void *YYPARSE_PARAM;
-#endif
-#else /* ! YYPARSE_PARAM */
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-int
-yyparse (void)
-#else
-int
-yyparse ()
-#endif
-#endif
+static bool parseRef(ParseTreeNode* parentNode)
{
-
- int yystate;
- int yyn;
- int yyresult;
- /* Number of tokens to shift before error messages enabled. */
- int yyerrstatus;
- /* Look-ahead token as an internal (translated) token number. */
- int yytoken = 0;
-#if YYERROR_VERBOSE
- /* Buffer for error messages, and its allocated size. */
- char yymsgbuf[128];
- char *yymsg = yymsgbuf;
- YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
-
- /* Three stacks and their tools:
- `yyss': related to states,
- `yyvs': related to semantic values,
- `yyls': related to locations.
-
- Refer to the stacks thru separate pointers, to allow yyoverflow
- to reallocate them elsewhere. */
-
- /* The state stack. */
- yytype_int16 yyssa[YYINITDEPTH];
- yytype_int16 *yyss = yyssa;
- yytype_int16 *yyssp;
-
- /* The semantic value stack. */
- YYSTYPE yyvsa[YYINITDEPTH];
- YYSTYPE *yyvs = yyvsa;
- YYSTYPE *yyvsp;
-
-
-
-#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
- YYSIZE_T yystacksize = YYINITDEPTH;
-
- /* The variables used to return semantic value and location from the
- action routines. */
- YYSTYPE yyval;
+ ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0);
+ ParseTreeNode* newParent = parentNode;
- /* The number of symbols on the RHS of the reduced rule.
- Keep to zero when no symbol should be popped. */
- int yylen = 0;
+ bool found;
- YYDPRINTF ((stderr, "Starting parse\n"));
+ if (said_tokens[said_token] == TOKEN_LT) {
+ said_token++;
- yystate = 0;
- yyerrstatus = 0;
- yynerrs = 0;
- yychar = YYEMPTY; /* Cause a token to be read. */
+ found = parseList(newNode);
- /* Initialize stack pointers.
- Waste one element of value and location stack
- so that they stay on the same level as the state stack.
- The wasted elements are never initialized. */
+ if (found) {
- yyssp = yyss;
- yyvsp = yyvs;
+ said_attach_subtree(newParent, 0x144, 0x14f, newNode);
- goto yysetstate;
+ newParent = newParent->right;
+
+ newNode = said_branch_node(said_next_node(), 0, 0);
-/*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate. |
-`------------------------------------------------------------*/
- yynewstate:
- /* In all cases, when you get here, the value and location stacks
- have just been pushed. So pushing a state here evens the stacks. */
- yyssp++;
-
- yysetstate:
- *yyssp = yystate;
-
- if (yyss + yystacksize - 1 <= yyssp)
- {
- /* Get the current used size of the three stacks, in elements. */
- YYSIZE_T yysize = yyssp - yyss + 1;
-
-#ifdef yyoverflow
- {
- /* Give user a chance to reallocate the stack. Use copies of
- these so that the &'s don't force the real ones into
- memory. */
- YYSTYPE *yyvs1 = yyvs;
- yytype_int16 *yyss1 = yyss;
-
-
- /* Each stack pointer address is followed by the size of the
- data in use in that stack, in bytes. This used to be a
- conditional around just the two extra args, but that might
- be undefined if yyoverflow is a macro. */
- yyoverflow (YY_("memory exhausted"),
- &yyss1, yysize * sizeof (*yyssp),
- &yyvs1, yysize * sizeof (*yyvsp),
-
- &yystacksize);
-
- yyss = yyss1;
- yyvs = yyvs1;
- }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
- goto yyexhaustedlab;
-# else
- /* Extend the stack our own way. */
- if (YYMAXDEPTH <= yystacksize)
- goto yyexhaustedlab;
- yystacksize *= 2;
- if (YYMAXDEPTH < yystacksize)
- yystacksize = YYMAXDEPTH;
-
- {
- yytype_int16 *yyss1 = yyss;
- union yyalloc *yyptr =
- (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
- if (! yyptr)
- goto yyexhaustedlab;
- YYSTACK_RELOCATE (yyss);
- YYSTACK_RELOCATE (yyvs);
-
-# undef YYSTACK_RELOCATE
- if (yyss1 != yyssa)
- YYSTACK_FREE (yyss1);
- }
-# endif
-#endif /* no yyoverflow */
-
- yyssp = yyss + yysize - 1;
- yyvsp = yyvs + yysize - 1;
-
-
- YYDPRINTF ((stderr, "Stack size increased to %lu\n",
- (unsigned long int) yystacksize));
-
- if (yyss + yystacksize - 1 <= yyssp)
- YYABORT;
- }
-
- YYDPRINTF ((stderr, "Entering state %d\n", yystate));
-
- goto yybackup;
-
-/*-----------.
-| yybackup. |
-`-----------*/
-yybackup:
-
- /* Do appropriate processing given the current state. Read a
- look-ahead token if we need one and don't already have one. */
-
- /* First try to decide what to do without reference to look-ahead token. */
- yyn = yypact[yystate];
- if (yyn == YYPACT_NINF)
- goto yydefault;
-
- /* Not known => get a look-ahead token if don't already have one. */
-
- /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
- if (yychar == YYEMPTY)
- {
- YYDPRINTF ((stderr, "Reading a token: "));
- yychar = YYLEX;
- }
-
- if (yychar <= YYEOF)
- {
- yychar = yytoken = YYEOF;
- YYDPRINTF ((stderr, "Now at end of input.\n"));
- }
- else
- {
- yytoken = YYTRANSLATE (yychar);
- YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
- }
-
- /* If the proper action on seeing token YYTOKEN is to reduce or to
- detect an error, take that action. */
- yyn += yytoken;
- if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
- goto yydefault;
- yyn = yytable[yyn];
- if (yyn <= 0)
- {
- if (yyn == 0 || yyn == YYTABLE_NINF)
- goto yyerrlab;
- yyn = -yyn;
- goto yyreduce;
- }
+ found = parseRef(newNode);
- if (yyn == YYFINAL)
- YYACCEPT;
+ if (found) {
- /* Count tokens shifted since error; after three, turn off error
- status. */
- if (yyerrstatus)
- yyerrstatus--;
+ said_attach_subtree(newParent, 0x141, 0x144, newNode);
- /* Shift the look-ahead token. */
- YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+ }
- /* Discard the shifted token unless it is eof. */
- if (yychar != YYEOF)
- yychar = YYEMPTY;
+ return true;
- yystate = yyn;
- *++yyvsp = yylval;
+ }
- goto yynewstate;
+ }
+ // NB: This is not an "else if'.
+ // If there is a "< [ ... ]", that is parsed as "< ..."
-/*-----------------------------------------------------------.
-| yydefault -- do the default action for the current state. |
-`-----------------------------------------------------------*/
-yydefault:
- yyn = yydefact[yystate];
- if (yyn == 0)
- goto yyerrlab;
- goto yyreduce;
+ if (said_tokens[said_token] == TOKEN_BRACKETO) {
+ said_token++;
+
+ found = parseRef(newNode);
+ if (found) {
-/*-----------------------------.
-| yyreduce -- Do a reduction. |
-`-----------------------------*/
-yyreduce:
- /* yyn is the number of a rule to reduce with. */
- yylen = yyr2[yyn];
+ if (said_tokens[said_token] == TOKEN_BRACKETC) {
+ said_token++;
- /* If YYLEN is nonzero, implement the default value of the action:
- `$$ = $1'.
+ said_attach_subtree(parentNode, 0x152, 0x144, newNode);
- Otherwise, the following line sets YYVAL to garbage.
- This behavior is undocumented and Bison
- users should not rely upon it. Assigning to YYVAL
- unconditionally makes the parser a bit smaller, and it avoids a
- GCC warning that YYVAL may be used uninitialized. */
- yyval = yyvsp[1-yylen];
+ return true;
+ }
+ }
+ }
- YY_REDUCE_PRINT (yyn);
- switch (yyn)
- {
- case 2:
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
- { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]))); ;}
- break;
+static bool parseComma(ParseTreeNode* parentNode)
+{
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
- case 3:
+ if (said_tokens[said_token] == TOKEN_COMMA) {
+ said_token++;
- { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (3)]), said_attach_branch((yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])))); ;}
- break;
+ bool found = parseList(parentNode);
- case 4:
+ if (found)
+ return true;
- { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (4)]), said_attach_branch((yyvsp[(2) - (4)]), said_attach_branch((yyvsp[(3) - (4)]), (yyvsp[(4) - (4)]))))); ;}
- break;
+ }
- case 5:
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
- { (yyval) = SAID_BRANCH_NULL; ;}
- break;
+static bool parseListEntry(ParseTreeNode* parentNode)
+{
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
- case 6:
+ ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0);
- { (yyval) = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); ;}
- break;
+ bool found;
- case 7:
+ if (said_tokens[said_token] == TOKEN_BRACKETO) {
+ said_token++;
- { (yyval) = SAID_BRANCH_NULL; ;}
- break;
+ found = parseExpr(newNode);
- case 8:
+ if (found) {
- { (yyval) = said_paren(said_value(0x141, said_value(0x149, (yyvsp[(1) - (1)]))), SAID_BRANCH_NULL); ;}
- break;
+ if (said_tokens[said_token] == TOKEN_BRACKETC) {
+ said_token++;
- case 9:
+ said_attach_subtree(parentNode, 0x152, 0x14c, newNode);
- { (yyval) = said_aug_branch(0x142, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); ;}
- break;
+ return true;
+ }
+ }
- case 10:
+ } else if (said_tokens[said_token] == TOKEN_PARENO) {
+ said_token++;
- { (yyval) = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;}
- break;
+ found = parseExpr(newNode);
- case 11:
+ if (found) {
- { (yyval) = SAID_BRANCH_NULL; ;}
- break;
+ if (said_tokens[said_token] == TOKEN_PARENC) {
+ said_token++;
- case 12:
+ said_attach_subtree(parentNode, 0x141, 0x14c, newNode);
- { (yyval) = said_aug_branch(0x143, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); ;}
- break;
+ return true;
+ }
+ }
- case 13:
+ } else if (parseWord(newNode)) {
- { (yyval) = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;}
- break;
+ said_attach_subtree(parentNode, 0x141, 0x153, newNode);
- case 14:
+ return true;
- { (yyval) = SAID_BRANCH_NULL; ;}
- break;
+ }
- case 15:
- { (yyval) = said_paren(said_value(0x141, said_value(0x153, said_terminal((yyvsp[(1) - (1)])))), SAID_BRANCH_NULL); ;}
- break;
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
- case 16:
+static bool parseList(ParseTreeNode* parentNode)
+{
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
- { (yyval) = said_aug_branch(0x141, 0x14f, (yyvsp[(1) - (1)]), SAID_BRANCH_NULL); ;}
- break;
+ bool found;
- case 17:
+ ParseTreeNode* newParent = parentNode;
- { (yyval) = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;}
- break;
+ found = parseListEntry(newParent);
- case 18:
+ if (found) {
- { (yyval) = (yyvsp[(1) - (1)]); ;}
- break;
+ newParent = newParent->right;
- case 19:
+ found = parseComma(newParent);
- { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); ;}
- break;
+ return true;
- case 20:
+ }
- { (yyval) = said_attach_branch((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); ;}
- break;
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
- case 21:
+static bool parseExpr(ParseTreeNode* parentNode)
+{
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
- { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); ;}
- break;
+ ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0);
- case 22:
+ bool ret = false;
+ bool found;
- { (yyval) = said_attach_branch((yyvsp[(1) - (5)]), (yyvsp[(3) - (5)])); ;}
- break;
+ ParseTreeNode* newParent = parentNode;
- case 23:
+ found = parseList(newNode);
- { (yyval) = said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); ;}
- break;
+ if (found) {
+ ret = true;
- case 24:
+ said_attach_subtree(newParent, 0x141, 0x14F, newNode);
- { (yyval) = (yyvsp[(1) - (1)]); ;}
- break;
+ newParent = newParent->right;
- case 25:
+ }
- { (yyval) = (yyvsp[(1) - (1)]); ;}
- break;
+ found = parseRef(newParent);
- case 26:
+ if (found || ret)
+ return true;
- { (yyval) = (yyvsp[(1) - (1)]); ;}
- break;
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
- case 27:
+static bool parseSpec(ParseTreeNode* parentNode)
+{
+ // Store current state for rolling back if we fail
+ int curToken = said_token;
+ int curTreePos = said_tree_pos;
+ ParseTreeNode* curRightChild = parentNode->right;
- { (yyval) = said_aug_branch(0x152, 0x144, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); ;}
- break;
+ ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0);
- case 28:
+ bool ret = false;
- { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), said_aug_branch(0x152, 0x144, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL)); ;}
- break;
+ bool found;
- case 29:
+ ParseTreeNode* newParent = parentNode;
- { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])); ;}
- break;
+ found = parseExpr(newNode);
- case 30:
+ if (found) {
+ // Sentence part 1 found
+ said_attach_subtree(newParent, 0x141, 0x149, newNode);
- { (yyval) = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;}
- break;
+ newParent = newParent->right;
- case 31:
+ ret = true;
+ }
- { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); ;}
- break;
+ bool nonempty;
- case 32:
+ found = parsePart2(newParent, nonempty);
- { (yyval) = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;}
- break;
+ if (found) {
- case 33:
+ ret = true;
- { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), (yyvsp[(3) - (3)])); ;}
- break;
+ if (nonempty) // non-empty part found
+ newParent = newParent->right;
- case 34:
- { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;}
- break;
+ found = parsePart3(newParent, nonempty);
- case 35:
+ if (found) {
- { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL); ;}
- break;
+ if (nonempty)
+ newParent = newParent->right;
+ }
+ }
+ if (said_tokens[said_token] == TOKEN_GT) {
+ said_token++;
-/* Line 1267 of yacc.c. */
+ newNode = said_branch_node(said_next_node(), 0,
+ said_leaf_node(said_next_node(), TOKEN_GT));
- default: break;
- }
- YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+ said_attach_subtree(newParent, 0x14B, TOKEN_GT, newNode);
- YYPOPSTACK (yylen);
- yylen = 0;
- YY_STACK_PRINT (yyss, yyssp);
+ }
- *++yyvsp = yyval;
+ if (ret)
+ return true;
- /* Now `shift' the result of the reduction. Determine what state
- that goes to, based on the state we popped back to and the rule
- number reduced by. */
+ // Rollback
+ said_token = curToken;
+ said_tree_pos = curTreePos;
+ parentNode->right = curRightChild;
+ return false;
+}
- yyn = yyr1[yyn];
- yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
- if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
- yystate = yytable[yystate];
- else
- yystate = yydefgoto[yyn - YYNTOKENS];
+static bool buildSaidTree() {
+ said_branch_node(said_tree, &said_tree[1], &said_tree[2]);
+ said_leaf_node(&said_tree[1], 0x141); // Magic number #1
+ said_branch_node(&said_tree[2], &said_tree[3], 0);
+ said_leaf_node(&said_tree[3], 0x13f); // Magic number #2
- goto yynewstate;
+ said_tree_pos = SAID_TREE_START;
+ bool ret = parseSpec(&said_tree[2]);
-/*------------------------------------.
-| yyerrlab -- here on detecting error |
-`------------------------------------*/
-yyerrlab:
- /* If not already recovering from an error, report this error. */
- if (!yyerrstatus)
- {
- ++yynerrs;
-#if ! YYERROR_VERBOSE
- yyerror (YY_("syntax error"));
-#else
- {
- YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
- if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
- {
- YYSIZE_T yyalloc = 2 * yysize;
- if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
- yyalloc = YYSTACK_ALLOC_MAXIMUM;
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yyalloc);
- if (yymsg)
- yymsg_alloc = yyalloc;
- else
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- }
- }
-
- if (0 < yysize && yysize <= yymsg_alloc)
- {
- (void) yysyntax_error (yymsg, yystate, yychar);
- yyerror (yymsg);
- }
- else
- {
- yyerror (YY_("syntax error"));
- if (yysize != 0)
- goto yyexhaustedlab;
- }
- }
-#endif
- }
+ if (!ret)
+ return false;
+ if (said_tokens[said_token] != TOKEN_TERM) {
+ // No terminator, so parse error.
+ // Rollback
+ said_tree[2].right = 0;
+ said_token = 0;
+ said_tree_pos = SAID_TREE_START;
+ return false;
+ }
- if (yyerrstatus == 3)
- {
- /* If just tried and failed to reuse look-ahead token after an
- error, discard it. */
+ return true;
+}
- if (yychar <= YYEOF)
- {
- /* Return failure if at end of input. */
- if (yychar == YYEOF)
- YYABORT;
- }
- else
- {
- yydestruct ("Error: discarding",
- yytoken, &yylval);
- yychar = YYEMPTY;
- }
- }
-
- /* Else will try to reuse look-ahead token after shifting the error
- token. */
- goto yyerrlab1;
-
-
-/*---------------------------------------------------.
-| yyerrorlab -- error raised explicitly by YYERROR. |
-`---------------------------------------------------*/
-yyerrorlab:
-
- /* Pacify compilers like GCC when the user code never invokes
- YYERROR and the label yyerrorlab therefore never appears in user
- code. */
- if (/*CONSTCOND*/ 0)
- goto yyerrorlab;
-
- /* Do not reclaim the symbols of the rule which action triggered
- this YYERROR. */
- YYPOPSTACK (yylen);
- yylen = 0;
- YY_STACK_PRINT (yyss, yyssp);
- yystate = *yyssp;
- goto yyerrlab1;
-
-
-/*-------------------------------------------------------------.
-| yyerrlab1 -- common code for both syntax error and YYERROR. |
-`-------------------------------------------------------------*/
-yyerrlab1:
- yyerrstatus = 3; /* Each real token shifted decrements this. */
-
- for (;;)
- {
- yyn = yypact[yystate];
- if (yyn != YYPACT_NINF)
- {
- yyn += YYTERROR;
- if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
- {
- yyn = yytable[yyn];
- if (0 < yyn)
- break;
- }
- }
+static int said_parse_spec(const byte *spec) {
+ int nextitem;
- /* Pop the current state because it cannot handle the error token. */
- if (yyssp == yyss)
- YYABORT;
+ said_token = 0;
+ said_tokens_nr = 0;
+ said_tree_pos = SAID_TREE_START;
- yydestruct ("Error: popping",
- yystos[yystate], yyvsp);
- YYPOPSTACK (1);
- yystate = *yyssp;
- YY_STACK_PRINT (yyss, yyssp);
- }
+ do {
+ nextitem = *spec++;
+ if (nextitem < SAID_FIRST)
+ said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++;
+ else
+ said_tokens[said_tokens_nr++] = SAID_LONG(nextitem);
- if (yyn == YYFINAL)
- YYACCEPT;
+ } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS));
- *++yyvsp = yylval;
+ if (nextitem != SAID_TERM) {
+ warning("SAID spec is too long");
+ return 1;
+ }
+ if (!buildSaidTree()) {
+ warning("Error while parsing SAID spec");
+ return 1;
+ }
- /* Shift the error token. */
- YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+ return 0;
+}
- yystate = yyn;
- goto yynewstate;
+/**********************/
+/**** Augmentation ****/
+/**********************/
+static bool dontclaim;
+static int outputDepth;
-/*-------------------------------------.
-| yyacceptlab -- YYACCEPT comes here. |
-`-------------------------------------*/
-yyacceptlab:
- yyresult = 0;
- goto yyreturn;
+enum ScanSaidType {
+ SCAN_SAID_AND = 0,
+ SCAN_SAID_OR = 1
+};
-/*-----------------------------------.
-| yyabortlab -- YYABORT comes here. |
-`-----------------------------------*/
-yyabortlab:
- yyresult = 1;
- goto yyreturn;
+static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT);
+static int scanSaidChildren(ParseTreeNode* parseT, ParseTreeNode* saidT,
+ ScanSaidType type);
+static int scanParseChildren(ParseTreeNode* parseT, ParseTreeNode* saidT);
-#ifndef yyoverflow
-/*-------------------------------------------------.
-| yyexhaustedlab -- memory exhaustion comes here. |
-`-------------------------------------------------*/
-yyexhaustedlab:
- yyerror (YY_("memory exhausted"));
- yyresult = 2;
- /* Fall through. */
-#endif
-yyreturn:
- if (yychar != YYEOF && yychar != YYEMPTY)
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval);
- /* Do not reclaim the symbols of the rule which action triggered
- this YYABORT or YYACCEPT. */
- YYPOPSTACK (yylen);
- YY_STACK_PRINT (yyss, yyssp);
- while (yyssp != yyss)
- {
- yydestruct ("Cleanup: popping",
- yystos[*yyssp], yyvsp);
- YYPOPSTACK (1);
- }
-#ifndef yyoverflow
- if (yyss != yyssa)
- YYSTACK_FREE (yyss);
-#endif
-#if YYERROR_VERBOSE
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
-#endif
- /* Make sure YYID is used. */
- return YYID (yyresult);
+static int node_major(ParseTreeNode* node) {
+ assert(node->type == kParseTreeBranchNode);
+ assert(node->left->type == kParseTreeLeafNode);
+ return node->left->value;
+}
+static int node_minor(ParseTreeNode* node) {
+ assert(node->type == kParseTreeBranchNode);
+ assert(node->right->type == kParseTreeBranchNode);
+ assert(node->right->left->type == kParseTreeLeafNode);
+ return node->right->left->value;
+}
+static bool node_is_terminal(ParseTreeNode* node) {
+ return (node->right->right &&
+ node->right->right->type != kParseTreeBranchNode);
+}
+static int node_terminal_value(ParseTreeNode* node) {
+ assert(node_is_terminal(node));
+ return node->right->right->value;
+}
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+static void node_print_desc(ParseTreeNode* node) {
+ assert(node);
+ assert(node->left);
+ if (node->left->type == kParseTreeBranchNode) {
+ scidprintf("< ");
+ node_print_desc(node->left);
+ scidprintf(", ...>");
+ } else {
+ if (node_is_terminal(node)) {
+ scidprintf("(%03x %03x %03x)", node_major(node),
+ node_minor(node),
+ node_terminal_value(node));
+ } else {
+ scidprintf("(%03x %03x <...>)", node_major(node),
+ node_minor(node));
+ }
+ }
}
+#else
+static void node_print_desc(ParseTreeNode*) { }
+#endif
+static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT)
+{
+ outputDepth++;
+ scidprintf("%*smatchTrees on ", outputDepth, "");
+ node_print_desc(parseT);
+ scidprintf(" and ");
+ node_print_desc(saidT);
+ scidprintf("\n");
-int parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC, YY_HASH, YY_LT, YY_GT};
+ bool inParen = node_minor(saidT) == 0x14F || node_minor(saidT) == 0x150;
+ bool inBracket = node_major(saidT) == 0x152;
-static int yylex() {
- int retval = said_tokens[said_token++];
+ int ret;
- if (retval < SAID_LONG(SAID_FIRST)) {
- yylval = retval;
- retval = WGROUP;
- } else {
- retval >>= 8;
-
- if (retval == SAID_TERM)
- retval = 0;
- else {
- assert(retval >= SAID_FIRST);
- retval = parse_yy_token_lookup[retval - SAID_FIRST];
- if (retval == YY_BRACKETSO) {
- if ((said_tokens[said_token] >> 8) == SAID_LT)
- retval = YY_BRACKETSO_LT;
- else
- if ((said_tokens[said_token] >> 8) == SAID_SLASH)
- retval = YY_BRACKETSO_SLASH;
- } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) {
- retval = YY_LT_BRACKETSO;
- } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) {
- retval = YY_LT_PARENO;
- }
- }
+ if (node_major(parseT) != 0x141 &&
+ node_major(saidT) != 0x141 && node_major(saidT) != 0x152 &&
+ node_major(saidT) != node_major(parseT))
+ {
+ ret = -1;
}
- return retval;
-}
+ // parse major is 0x141 and/or
+ // said major is 0x141/0x152 and/or
+ // said major is parse major
-static int said_next_node() {
- return ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES)) ? said_tree_pos = 0 : said_tree_pos++;
-}
+ else if (node_is_terminal(saidT) && node_is_terminal(parseT) ) {
-#define SAID_NEXT_NODE said_next_node()
+ // both saidT and parseT are terminals
-static int said_leaf_node(tree_t pos, int value) {
- said_tree[pos].type = kParseTreeLeafNode;
+ int said_val = node_terminal_value(saidT);
+ int parse_val = node_terminal_value(parseT);
- if (value != VALUE_IGNORE)
- said_tree[pos].content.value = value;
+ if (said_val != WORD_NONE &&
+ (said_val == parse_val || said_val == WORD_ANY ||
+ parse_val == WORD_ANY))
+ ret = 1;
+ else
+ ret = -1;
- return pos;
-}
+ scidprintf("%*smatchTrees matching terminals: %03x vs %03x (%d)\n",
+ outputDepth, "", parse_val, said_val, ret);
-static int said_branch_node(tree_t pos, int left, int right) {
- said_tree[pos].type = kParseTreeBranchNode;
+ } else if (node_is_terminal(saidT) && !node_is_terminal(parseT)) {
- if (left != VALUE_IGNORE)
- said_tree[pos].content.branches[0] = left;
+ // saidT is a terminal, but parseT isn't
- if (right != VALUE_IGNORE)
- said_tree[pos].content.branches[1] = right;
+ if (node_major(parseT) == 0x141 ||
+ node_major(parseT) == node_major(saidT))
+ ret = scanParseChildren(parseT->right->right, saidT);
+ else
+ ret = 0;
- return pos;
-}
+ } else if (node_is_terminal(parseT)) {
-static tree_t said_paren(tree_t t1, tree_t t2) {
- if (t1)
- return said_branch_node(SAID_NEXT_NODE, t1, t2);
- else
- return t2;
-}
+ // parseT is a terminal, but saidT isn't
-static tree_t said_value(int val, tree_t t) {
- return said_branch_node(SAID_NEXT_NODE, said_leaf_node(SAID_NEXT_NODE, val), t);
+ if (node_major(saidT) == 0x141 || node_major(saidT) == 0x152 ||
+ node_major(saidT) == node_major(parseT))
+ ret = scanSaidChildren(parseT, saidT->right->right,
+ inParen ? SCAN_SAID_OR : SCAN_SAID_AND );
+ else
+ ret = 0;
-}
+ } else if (node_major(saidT) != 0x141 && node_major(saidT) != 0x152 &&
+ node_major(saidT) != node_major(parseT)) {
-static tree_t said_terminal(int val) {
- return said_leaf_node(SAID_NEXT_NODE, val);
-}
+ // parseT and saidT both aren't terminals
+ // said major is not 0x141 or 0x152 or parse major
-static tree_t said_aug_branch(int n1, int n2, tree_t t1, tree_t t2) {
- int retval;
+ ret = scanParseChildren(parseT->right->right, saidT);
- retval = said_branch_node(SAID_NEXT_NODE,
- said_branch_node(SAID_NEXT_NODE,
- said_leaf_node(SAID_NEXT_NODE, n1),
- said_branch_node(SAID_NEXT_NODE,
- said_leaf_node(SAID_NEXT_NODE, n2),
- t1)
- ),
- t2);
-
-#ifdef SAID_DEBUG
- fprintf(stderr, "AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval);
-#endif
+ } else {
- return retval;
-}
+ // parseT and saidT are both not terminals,
+ // said major 0x141 or 0x152 or equal to parse major
-static tree_t said_attach_branch(tree_t base, tree_t attacheant) {
-#ifdef SAID_DEBUG
- fprintf(stderr, "ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base);
-#endif
+ ret = scanSaidChildren(parseT->right->right, saidT->right->right,
+ inParen ? SCAN_SAID_OR : SCAN_SAID_AND);
- if (!attacheant)
- return base;
- if (!base)
- return attacheant;
+ }
- if (!base)
- return 0; // Happens if we're out of space
+ if (inBracket && ret == 0) {
+ scidprintf("%*smatchTrees changing ret to 1 due to brackets\n",
+ outputDepth, "");
+ ret = 1;
+ }
- said_branch_node(base, VALUE_IGNORE, attacheant);
+ scidprintf("%*smatchTrees returning %d\n", outputDepth, "", ret);
+ outputDepth--;
- return base;
+ return ret;
}
-static said_spec_t said_top_branch(tree_t first) {
-#ifdef SAID_DEBUG
- fprintf(stderr, "TOP([%04x])\n", first);
-#endif
- said_branch_node(0, 1, 2);
- said_leaf_node(1, 0x141); // Magic number #1
- said_branch_node(2, 3, first);
- said_leaf_node(3, 0x13f); // Magic number #2
- ++said_blessed;
+static int scanSaidChildren(ParseTreeNode* parseT, ParseTreeNode* saidT,
+ ScanSaidType type) {
+ outputDepth++;
+ scidprintf("%*sscanSaid(%s) on ", outputDepth, "",
+ type == SCAN_SAID_OR ? "OR" : "AND");
+ node_print_desc(parseT);
+ scidprintf(" and ");
+ node_print_desc(saidT);
+ scidprintf("\n");
- return 0;
-}
+ int ret = 1;
-static int said_parse_spec(byte *spec) {
- int nextitem;
+ assert(!(type == SCAN_SAID_OR && !saidT));
- said_parse_error = NULL;
- said_token = 0;
- said_tokens_nr = 0;
- said_blessed = 0;
+ while (saidT) {
+ assert(saidT->type == kParseTreeBranchNode);
- said_tree_pos = SAID_TREE_START;
+ ParseTreeNode* saidChild = saidT->left;
+ assert(saidChild);
- do {
- nextitem = *spec++;
- if (nextitem < SAID_FIRST)
- said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++;
- else
- said_tokens[said_tokens_nr++] = SAID_LONG(nextitem);
+ if (node_major(saidChild) != 0x145) {
- } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS));
+ ret = scanParseChildren(parseT, saidChild);
- if (nextitem == SAID_TERM)
- yyparse();
- else {
- warning("SAID spec is too long");
- return 1;
- }
+ if (type == SCAN_SAID_AND && ret != 1)
+ break;
- if (said_parse_error) {
- warning("Error while parsing SAID spec: %s", said_parse_error);
- free(said_parse_error);
- return 1;
- }
+ if (type == SCAN_SAID_OR && ret == 1)
+ break;
- if (said_tree_pos == 0) {
- warning("Out of tree space while parsing SAID spec");
- return 1;
- }
+ }
+
+ saidT = saidT->right;
- if (said_blessed != 1) {
- warning("Found multiple top branches");
- return 1;
}
+ scidprintf("%*sscanSaid returning %d\n", outputDepth, "", ret);
- return 0;
+ outputDepth--;
+ return ret;
}
-/**********************/
-/**** Augmentation ****/
-/**********************/
-
-// primitive functions
-#define AUG_READ_BRANCH(a, br, p) \
- if (tree[p].type != kParseTreeBranchNode) \
- return 0; \
- a = tree[p].content.branches[br];
+static int scanParseChildren(ParseTreeNode* parseT, ParseTreeNode* saidT) {
-#define AUG_READ_VALUE(a, p) \
- if (tree[p].type != kParseTreeLeafNode) \
- return 0; \
- a = tree[p].content.value;
+ outputDepth++;
+ scidprintf("%*sscanParse on ", outputDepth, "");
+ node_print_desc(parseT);
+ scidprintf(" and ");
+ node_print_desc(saidT);
+ scidprintf("\n");
-#define AUG_ASSERT(i) \
- if (!i) return 0;
-
-static int aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second) {
- // Returns the next sibling relative to the specified position in 'tree',
- // sets *first and *second to its augment node values, returns the new position
- // or 0 if there was no next sibling
- int seek, valpos;
+ if (node_major(saidT) == 0x14B) {
+ dontclaim = true;
+ scidprintf("%*sscanParse returning 1 (0x14B)\n", outputDepth, "");
+ outputDepth--;
+ return 1;
+ }
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(seek, 0, pos);
- AUG_ASSERT(seek);
+ bool inParen = node_minor(saidT) == 0x14F || node_minor(saidT) == 0x150;
+ bool inBracket = node_major(saidT) == 0x152;
- // Now retrieve first value
- AUG_READ_BRANCH(valpos, 0, seek);
- AUG_ASSERT(valpos);
- AUG_READ_VALUE(*first, valpos);
+ int ret;
- // Get second value
- AUG_READ_BRANCH(seek, 1, seek);
- AUG_ASSERT(seek);
- AUG_READ_BRANCH(valpos, 0, seek);
- AUG_ASSERT(valpos);
- AUG_READ_VALUE(*second, valpos);
+ // descend further down saidT before actually scanning parseT
+ if ((node_major(saidT) == 0x141 || node_major(saidT) == 0x152) &&
+ !node_is_terminal(saidT)) {
- return pos;
-}
+ ret = scanSaidChildren(parseT, saidT->right->right,
+ inParen ? SCAN_SAID_OR : SCAN_SAID_AND );
-static int aug_get_wgroup(parse_tree_node_t *tree, int pos) {
- // Returns 0 if pos in tree is not the root of a 3-element list, otherwise
- // it returns the last element (which, in practice, is the word group
- int val;
+ } else if (parseT && parseT->left->type == kParseTreeBranchNode) {
- AUG_READ_BRANCH(pos, 0, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
- AUG_READ_VALUE(val, pos);
+ ret = 0;
+ int subresult = 0;
- return val;
-}
+ while (parseT) {
+ assert(parseT->type == kParseTreeBranchNode);
-static int aug_get_base_node(parse_tree_node_t *tree) {
- int startpos = 0;
- AUG_READ_BRANCH(startpos, 1, startpos);
+ ParseTreeNode* parseChild = parseT->left;
+ assert(parseChild);
- return startpos;
-}
+ scidprintf("%*sscanning next: ", outputDepth, "");
+ node_print_desc(parseChild);
+ scidprintf("\n");
-// semi-primitive functions
+ if (node_major(parseChild) == node_major(saidT) ||
+ node_major(parseChild) == 0x141)
+ subresult = matchTrees(parseChild, saidT);
-static int aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second) {
- // like aug_get_next_sibling, except that it recurses into the tree and
- // finds the first child (usually *not* Ayanami Rei) of the current branch
- // rather than its next sibling.
- AUG_READ_BRANCH(pos, 0, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
+ if (subresult != 0)
+ ret = subresult;
- return aug_get_next_sibling(tree, pos, first, second);
-}
+ if (ret == 1)
+ break;
-static void aug_find_words_recursively(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr,
- int *ref_words, int *ref_words_nr, int maxwords, int refbranch) {
- // Finds and lists all base (141) and reference (144) words */
- int major, minor;
- int word;
- int pos = aug_get_first_child(tree, startpos, &major, &minor);
+ parseT = parseT->right;
- //if (major == WORD_TYPE_REF)
- // refbranch = 1;
+ }
- while (pos) {
- if ((word = aug_get_wgroup(tree, pos))) { // found a word
- if (!refbranch && major == WORD_TYPE_BASE) {
- if ((*base_words_nr) == maxwords) {
- warning("Out of regular words");
- return; // return gracefully
- }
+ // ret is now:
+ // 1 if ANY matchTrees(parseSibling, saidTree) returned 1
+ // ELSE: -1 if ANY returned -1
+ // ELSE: 0
- base_words[*base_words_nr] = word; // register word
- ++(*base_words_nr);
+ } else {
- }
- if (major == WORD_TYPE_REF || refbranch) {
- if ((*ref_words_nr) == maxwords) {
- warning("Out of reference words");
- return; // return gracefully
- }
+ ret = matchTrees(parseT, saidT);
- ref_words[*ref_words_nr] = word; // register word
- ++(*ref_words_nr);
+ }
- }
- if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF)
- warning("aug_find_words_recursively(): Unknown word type %03x", major);
+ if (inBracket && ret == 0) {
+ scidprintf("%*sscanParse changing ret to 1 due to brackets\n",
+ outputDepth, "");
+ ret = 1;
+ }
- } else // Did NOT find a word group: Attempt to recurse
- aug_find_words_recursively(tree, pos, base_words, base_words_nr,
- ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF);
+ scidprintf("%*sscanParse returning %d\n", outputDepth, "", ret);
+ outputDepth--;
- pos = aug_get_next_sibling(tree, pos, &major, &minor);
- }
+ return ret;
}
-static void aug_find_words(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr,
- int *ref_words, int *ref_words_nr, int maxwords) {
- // initializing wrapper for aug_find_words_recursively()
- *base_words_nr = 0;
- *ref_words_nr = 0;
- aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0);
-}
+static int augment_parse_nodes(ParseTreeNode *parseT, ParseTreeNode *saidT) {
+ outputDepth = 0;
+ scidprintf("augment_parse_nodes on ");
+ node_print_desc(parseT);
+ scidprintf(" and ");
+ node_print_desc(saidT);
+ scidprintf("\n");
+ dontclaim = false;
-static int aug_contains_word(int *list, int length, int word) {
- int i;
+ int ret = matchTrees(parseT, saidT);
- if (word == ANYWORD)
- return (length);
+ scidprintf("matchTrees returned %d\n", ret);
- for (i = 0; i < length; i++)
- if (list[i] == word)
- return 1;
+ if (ret != 1)
+ return 0;
- return 0;
+ if (dontclaim)
+ return SAID_PARTIAL_MATCH;
+
+ return 1;
}
-static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset,
- int parse_branch, int major, int minor, int *base_words, int base_words_nr,
- int *ref_words, int ref_words_nr);
+/*******************/
+/**** Main code ****/
+/*******************/
-static int augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset,
- int parse_basepos, int major, int minor,
- int *base_words, int base_words_nr, int *ref_words, int ref_words_nr) {
- int cmajor, cminor, cpos;
- cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor);
- if (!cpos) {
- warning("augment_match_expression_p(): Empty condition");
- return 1;
- }
+int said(EngineState *s, const byte *spec, bool verbose) {
+ int retval;
+ Vocabulary *voc = g_sci->getVocabulary();
- scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor);
-
- if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE))
- return augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr);
-
- switch (major) {
-
- case WORD_TYPE_BASE:
- while (cpos) {
- if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
- int word = aug_get_wgroup(saidt, cpos);
- scidprintf("Looking for word %03x\n", word);
-
- if (aug_contains_word(base_words, base_words_nr, word))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
- if (augment_sentence_expression(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
- int gc_major, gc_minor;
- int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
-
- while (gchild) {
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major,
- minor, base_words, base_words_nr,
- ref_words, ref_words_nr))
- return 1;
- gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
- }
- } else
- warning("augment_match_expression_p(): Unknown type 141 minor number %3x", cminor);
-
- cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
+ ParseTreeNode *parse_tree_ptr = voc->_parserNodes;
- }
- break;
-
- case WORD_TYPE_REF:
- while (cpos) {
- if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
- int word = aug_get_wgroup(saidt, cpos);
- scidprintf("Looking for refword %03x\n", word);
-
- if (aug_contains_word(ref_words, ref_words_nr, word))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
- int gc_major, gc_minor;
- int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
-
- while (gchild) {
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major,
- minor, base_words, base_words_nr,
- ref_words, ref_words_nr))
- return 1;
- gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
- }
- } else
- warning("augment_match_expression_p(): Unknown type 144 minor number %3x", cminor);
-
- cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
+ if (voc->parserIsValid) {
+ if (said_parse_spec(spec))
+ return SAID_NO_MATCH;
- }
- break;
+ if (verbose)
+ vocab_dump_parse_tree("Said-tree", said_tree);
+ retval = augment_parse_nodes(parse_tree_ptr, said_tree);
- case AUGMENT_SENTENCE_PART_BRACKETS:
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr))
- return 1;
+ if (!retval)
+ return SAID_NO_MATCH;
+ else if (retval != SAID_PARTIAL_MATCH)
+ return SAID_FULL_MATCH;
+ else
+ return SAID_PARTIAL_MATCH;
+ }
- scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor);
+ return SAID_NO_MATCH;
+}
- switch (cmajor) {
- case WORD_TYPE_BASE:
- if (!base_words_nr)
- return 1;
- break;
- case WORD_TYPE_REF:
- if (!ref_words_nr)
- return 1;
- break;
+/*
- default:
- warning("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x", cmajor);
- }
+Some test expressions for in the ScummVM debugging console, using
+Codename: ICEMAN's vocabulary:
- break;
- default:
- warning("augment_match_expression_p(): Unknown predicate %03x", major);
- }
+said green board & [!*] / 8af < 1f6
+True
- scidprintf("augment_match_expression_p(): Generic failure\n");
+said get green board & [!*] / 8af < 1f6
+False
- return 0;
-}
+said green board & [!*] / 8af [< 1f6 ]
+True
-static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset,
- int parse_branch, int major, int minor, int *base_words, int base_words_nr,
- int *ref_words, int ref_words_nr) {
- int check_major, check_minor;
- int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor);
- do {
- if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch, check_major, check_minor,
- base_words, base_words_nr, ref_words, ref_words_nr)))
- return 0;
- } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor)));
+said climb up & 19b , 426 [< 142 ] [/ 81e ]
+True
- return 1;
-}
+said climb up ladder & 19b , 426 [< 142 ] [/ 81e ]
+True
-static int augment_sentence_part(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, int parse_basepos, int major, int minor) {
- int pmajor, pminor;
- int parse_branch = parse_basepos;
- int optional = 0;
- int foundwords = 0;
+said climb down & 19b , 426 [< 142 ] [/ 81e ]
+False
- scidprintf("Augmenting (%03x %03x\n", major, minor);
+said climb up tree & 19b , 426 [< 142 ] [/ 81e ]
+False
- if (major == AUGMENT_SENTENCE_PART_BRACKETS) { // '[/ foo]' is true if '/foo' or if there
- // exists no x for which '/x' is true
- if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) {
- scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor);
- optional = 1;
- } else {
- scidprintf("Matched empty optional expression\n");
- return 1;
- }
- }
+said climb up & 19b , 446 , 426 [< 143 ] [/ 81e ]
+False
- if ((major < 0x141) || (major > 0x143)) {
- scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major);
- return 0;
- }
+said climb down & 19b , 446 , 426 [< 143 ] [/ 81e ]
+True
- while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor))) {
- if (pmajor == major) { // found matching sentence part
- int success;
- int base_words_nr;
- int ref_words_nr;
- int base_words[AUGMENT_MAX_WORDS];
- int ref_words[AUGMENT_MAX_WORDS];
-#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
- int i;
-#endif
+said use green device & 1a5 / 8c1 [< 21d ]
+False
- scidprintf("Found match with pminor = %03x\n", pminor);
- aug_find_words(parset, parse_branch, base_words, &base_words_nr, ref_words, &ref_words_nr, AUGMENT_MAX_WORDS);
- foundwords |= (ref_words_nr | base_words_nr);
-#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
- printf("%d base words:", base_words_nr);
- for (i = 0; i < base_words_nr; i++)
- printf(" %03x", base_words[i]);
- printf("\n%d reference words:", ref_words_nr);
- for (i = 0; i < ref_words_nr; i++)
- printf(" %03x", ref_words[i]);
- printf("\n");
-#endif
+said use electronic device & 1a5 / 8c1 [< 21d ]
+True
- success = augment_sentence_expression(saidt, augment_pos, parset, parse_basepos, major, minor,
- base_words, base_words_nr, ref_words, ref_words_nr);
+said use device & 1a5 / 8c1 [< 21d ]
+True
- if (success) {
- scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor);
- return 1;
- }
- }
- }
+said eat & 429 [/ !* ]
+True
- if (optional && (foundwords == 0)) {
- scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor);
- return 1;
- }
- scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor);
+said eat ladder & 429 [/ !* ]
+False
- return 0;
-}
+said look at the ladder & 3f8 / 81e [< !* ]
+True
-static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt) {
- int augment_basepos = 0;
- int parse_basepos;
- int major, minor;
- int dontclaim = 0;
+said look at the green ladder & 3f8 / 81e [< !* ]
+False
- parse_basepos = aug_get_base_node(parset);
- if (!parse_basepos) {
- warning("augment_parse_nodes(): Parse tree is corrupt");
- return 0;
- }
+said look green book & / 7f6 [< 8d2 ]
+False
- augment_basepos = aug_get_base_node(saidt);
- if (!augment_basepos) {
- warning("augment_parse_nodes(): Said tree is corrupt");
- return 0;
- }
+said look green book & 3f8 [< ca ]
+True
- while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) {
- if ((major == 0x14b) && (minor == SAID_LONG(SAID_GT)))
- dontclaim = 1; // special case
- else // normal sentence part
- if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) {
- scidprintf("Returning failure\n");
- return 0; // fail
- }
- }
+said get a blue board for the green ladder & 3f9 / 8af [ < 1f6 ] / 81e < 1f6
+False
- scidprintf("Returning success with dontclaim=%d\n", dontclaim);
+said get a board for the green ladder & 3f9 / 8af [ < 1f6 ] / 81e < 1f6
+True
- if (dontclaim)
- return SAID_PARTIAL_MATCH;
- else
- return 1; // full match
-}
+said get a blue board & 3f9 / 8af [ < 1f6 ]
+False
+said get up & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ]
+True
-/*******************/
-/**** Main code ****/
-/*******************/
+said get left & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ]
+False
-int said(EngineState *s, byte *spec, bool verbose) {
- int retval;
+said look down & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ]
+True
- parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes;
+said get & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ]
+True
- if (s->_voc->parserIsValid) {
- if (said_parse_spec(spec)) {
- printf("Offending spec was: ");
- s->_voc->decipherSaidBlock(spec);
- return SAID_NO_MATCH;
- }
+said put washer on shaft & 455 , ( 3fa < cb ) / 8c6
+True
- if (verbose)
- vocab_dump_parse_tree("Said-tree", said_tree); // Nothing better to do yet
- retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0]));
+said depth correct & [!*] < 8b1 / 22
+True
- if (!retval)
- return SAID_NO_MATCH;
- else if (retval != SAID_PARTIAL_MATCH)
- return SAID_FULL_MATCH;
- else
- return SAID_PARTIAL_MATCH;
- }
+said depth acknowledged & / 46d , 460 , 44d < 8b1
+True
- return SAID_NO_MATCH;
-}
+said depth confirmed & / 46d , 460 , 44d < 8b1
+True
+
+said depth attained & / 46d , 460 , 44d < 8b1
+True
+
+
+*/
-#ifdef SAID_DEBUG_PROGRAM
-int main (int argc, char *argv) {
- byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff};
- EngineState s;
- s.parser_valid = 1;
- said(&s, block);
-}
-#endif
} // End of namespace Sci
diff --git a/engines/sci/parser/said.y b/engines/sci/parser/said.y
deleted file mode 100644
index 27486c5794..0000000000
--- a/engines/sci/parser/said.y
+++ /dev/null
@@ -1,838 +0,0 @@
-%{
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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 "sci/engine/state.h"
-
-
-// Bison generates an empty switch statement that gives a warning in MSVC.
-// This disables that warning.
-#ifdef _MSC_VER
-#pragma warning(disable:4065)
-#endif
-
-
-namespace Sci {
-
-#define SAID_BRANCH_NULL 0
-
-#define MAX_SAID_TOKENS 128
-
-// Maximum number of words to be expected in a parsed sentence
-#define AUGMENT_MAX_WORDS 64
-
-
-#define ANYWORD 0xfff
-
-#define WORD_TYPE_BASE 0x141
-#define WORD_TYPE_REF 0x144
-#define WORD_TYPE_SYNTACTIC_SUGAR 0x145
-
-#define AUGMENT_SENTENCE_PART_BRACKETS 0x152
-
-// Minor numbers
-#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c
-#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153
-#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144
-#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f
-
-
-#undef YYDEBUG /*1*/
-//#define SAID_DEBUG*/
-//#define SCI_DEBUG_PARSE_TREE_AUGMENTATION // uncomment to debug parse tree augmentation
-
-
-#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
-#define scidprintf printf
-#else
-void print_nothing(...) { }
-#define scidprintf print_nothing
-#endif
-
-
-static char *said_parse_error;
-
-static int said_token;
-static int said_tokens_nr;
-static int said_tokens[MAX_SAID_TOKENS];
-static int said_blessed; // increminated by said_top_branch
-
-static int said_tree_pos; // Set to 0 if we're out of space
-#define SAID_TREE_START 4; // Reserve space for the 4 top nodes
-
-#define VALUE_IGNORE -424242
-
-static parse_tree_node_t said_tree[VOCAB_TREE_NODES];
-
-typedef int wgroup_t;
-typedef int tree_t;
-typedef int said_spec_t;
-
-static tree_t said_aug_branch(int, int, tree_t, tree_t);
-static tree_t said_attach_branch(tree_t, tree_t);
-/*
-static tree_t said_wgroup_branch(wgroup_t);
-*/
-static said_spec_t said_top_branch(tree_t);
-static tree_t said_paren(tree_t, tree_t);
-static tree_t said_value(int, tree_t);
-static tree_t said_terminal(int);
-
-static int yylex();
-
-static int yyerror(const char *s) {
- said_parse_error = strdup(s);
- return 1; /* Abort */
-}
-
-%}
-
-%token WGROUP /* Word group */
-%token YY_COMMA /* 0xf0 */
-%token YY_AMP /* 0xf1 */
-%token YY_SLASH /* 0xf2 */
-%token YY_PARENO /* 0xf3 */
-%token YY_PARENC /* 0xf4 */
-%token YY_BRACKETSO /* 0xf5 */
-%token YY_BRACKETSC /* 0xf6 */
-%token YY_HASH /* 0xf7 */
-%token YY_LT /* 0xf8 */
-%token YY_GT /* 0xf9 */
-%token YY_BRACKETSO_LT /* special token used to imitate LR(2) behaviour */
-%token YY_BRACKETSO_SLASH /* special token used to imitate LR(2) behaviour */
-%token YY_LT_BRACKETSO /* special token used to imitate LR(2) behaviour */
-%token YY_LT_PARENO /* special token used to imitate LR(2) behaviour */
-
-%%
-
-saidspec : leftspec optcont
- { $$ = said_top_branch(said_attach_branch($1, $2)); }
- | leftspec midspec optcont
- { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, $3))); }
- | leftspec midspec rightspec optcont
- { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, said_attach_branch($3, $4)))); }
- ;
-
-
-optcont : /* empty */
- { $$ = SAID_BRANCH_NULL; }
- | YY_GT
- { $$ = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); }
- ;
-
-
-
-leftspec : /* empty */
- { $$ = SAID_BRANCH_NULL; }
- | expr
- { $$ = said_paren(said_value(0x141, said_value(0x149, $1)), SAID_BRANCH_NULL); }
- ;
-
-
-
-midspec : YY_SLASH expr
- { $$ = said_aug_branch(0x142, 0x14a, $2, SAID_BRANCH_NULL); }
- | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC
- { $$ = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
- | YY_SLASH
- { $$ = SAID_BRANCH_NULL; }
- ;
-
-
-
-rightspec : YY_SLASH expr
- { $$ = said_aug_branch(0x143, 0x14a, $2, SAID_BRANCH_NULL); }
- | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC
- { $$ = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
- | YY_SLASH
- { $$ = SAID_BRANCH_NULL; }
- ;
-
-
-word : WGROUP
- { $$ = said_paren(said_value(0x141, said_value(0x153, said_terminal($1))), SAID_BRANCH_NULL); }
- ;
-
-
-cwordset : wordset
- { $$ = said_aug_branch(0x141, 0x14f, $1, SAID_BRANCH_NULL); }
- | YY_BRACKETSO wordset YY_BRACKETSC
- { $$ = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
- ;
-
-
-wordset : word
- { $$ = $1; }
- | YY_PARENO expr YY_PARENC
- { $$ = said_aug_branch(0x141, 0x14c, $2, SAID_BRANCH_NULL); }
- | wordset YY_COMMA wordset
- { $$ = said_attach_branch($1, $3); }
- | wordset YY_BRACKETSO_LT wordrefset YY_BRACKETSC
- { $$ = said_attach_branch($1, $3); }
- | wordset YY_COMMA YY_BRACKETSO wordset YY_BRACKETSC
- { $$ = said_attach_branch($1, $3); }
- ;
-
-
-expr : cwordset cwordrefset
- { $$ = said_attach_branch($1, $2); }
- | cwordset
- { $$ = $1; }
- | cwordrefset
- { $$ = $1; }
- ;
-
-
-cwordrefset : wordrefset
- { $$ = $1; }
- | YY_BRACKETSO_LT wordrefset YY_BRACKETSC
- { $$ = said_aug_branch(0x152, 0x144, $2, SAID_BRANCH_NULL); }
- | wordrefset YY_BRACKETSO_LT wordrefset YY_BRACKETSC
- { $$ = said_attach_branch($1, said_aug_branch(0x152, 0x144, $3, SAID_BRANCH_NULL)); }
- ;
-
-
-wordrefset : YY_LT word recref
- { $$ = said_aug_branch(0x144, 0x14f, $2, $3); }
- | YY_LT_PARENO YY_PARENO expr YY_PARENC
- { $$ = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
- | YY_LT wordset
- { $$ = said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL); }
- | YY_LT_BRACKETSO YY_BRACKETSO wordset YY_BRACKETSC
- { $$ = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
- ;
-
-
-recref : YY_LT wordset recref
- { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), $3); }
- | YY_LT wordset
- { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
- | YY_LT_PARENO YY_PARENO expr YY_PARENC
- { $$ = said_aug_branch(0x141, 0x14c, $2, SAID_BRANCH_NULL); }
- ;
-
-%%
-
-int parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC, YY_HASH, YY_LT, YY_GT};
-
-static int yylex() {
- int retval = said_tokens[said_token++];
-
- if (retval < SAID_LONG(SAID_FIRST)) {
- yylval = retval;
- retval = WGROUP;
- } else {
- retval >>= 8;
-
- if (retval == SAID_TERM)
- retval = 0;
- else {
- assert(retval >= SAID_FIRST);
- retval = parse_yy_token_lookup[retval - SAID_FIRST];
- if (retval == YY_BRACKETSO) {
- if ((said_tokens[said_token] >> 8) == SAID_LT)
- retval = YY_BRACKETSO_LT;
- else
- if ((said_tokens[said_token] >> 8) == SAID_SLASH)
- retval = YY_BRACKETSO_SLASH;
- } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) {
- retval = YY_LT_BRACKETSO;
- } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) {
- retval = YY_LT_PARENO;
- }
- }
- }
-
- return retval;
-}
-
-static int said_next_node() {
- return ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES)) ? said_tree_pos = 0 : said_tree_pos++;
-}
-
-#define SAID_NEXT_NODE said_next_node()
-
-static int said_leaf_node(tree_t pos, int value) {
- said_tree[pos].type = kParseTreeLeafNode;
-
- if (value != VALUE_IGNORE)
- said_tree[pos].content.value = value;
-
- return pos;
-}
-
-static int said_branch_node(tree_t pos, int left, int right) {
- said_tree[pos].type = kParseTreeBranchNode;
-
- if (left != VALUE_IGNORE)
- said_tree[pos].content.branches[0] = left;
-
- if (right != VALUE_IGNORE)
- said_tree[pos].content.branches[1] = right;
-
- return pos;
-}
-
-static tree_t said_paren(tree_t t1, tree_t t2) {
- if (t1)
- return said_branch_node(SAID_NEXT_NODE, t1, t2);
- else
- return t2;
-}
-
-static tree_t said_value(int val, tree_t t) {
- return said_branch_node(SAID_NEXT_NODE, said_leaf_node(SAID_NEXT_NODE, val), t);
-
-}
-
-static tree_t said_terminal(int val) {
- return said_leaf_node(SAID_NEXT_NODE, val);
-}
-
-static tree_t said_aug_branch(int n1, int n2, tree_t t1, tree_t t2) {
- int retval;
-
- retval = said_branch_node(SAID_NEXT_NODE,
- said_branch_node(SAID_NEXT_NODE,
- said_leaf_node(SAID_NEXT_NODE, n1),
- said_branch_node(SAID_NEXT_NODE,
- said_leaf_node(SAID_NEXT_NODE, n2),
- t1)
- ),
- t2);
-
-#ifdef SAID_DEBUG
- fprintf(stderr, "AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval);
-#endif
-
- return retval;
-}
-
-static tree_t said_attach_branch(tree_t base, tree_t attacheant) {
-#ifdef SAID_DEBUG
- fprintf(stderr, "ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base);
-#endif
-
- if (!attacheant)
- return base;
- if (!base)
- return attacheant;
-
- if (!base)
- return 0; // Happens if we're out of space
-
- said_branch_node(base, VALUE_IGNORE, attacheant);
-
- return base;
-}
-
-static said_spec_t said_top_branch(tree_t first) {
-#ifdef SAID_DEBUG
- fprintf(stderr, "TOP([%04x])\n", first);
-#endif
- said_branch_node(0, 1, 2);
- said_leaf_node(1, 0x141); // Magic number #1
- said_branch_node(2, 3, first);
- said_leaf_node(3, 0x13f); // Magic number #2
-
- ++said_blessed;
-
- return 0;
-}
-
-static int said_parse_spec(byte *spec) {
- int nextitem;
-
- said_parse_error = NULL;
- said_token = 0;
- said_tokens_nr = 0;
- said_blessed = 0;
-
- said_tree_pos = SAID_TREE_START;
-
- do {
- nextitem = *spec++;
- if (nextitem < SAID_FIRST)
- said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++;
- else
- said_tokens[said_tokens_nr++] = SAID_LONG(nextitem);
-
- } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS));
-
- if (nextitem == SAID_TERM)
- yyparse();
- else {
- warning("SAID spec is too long");
- return 1;
- }
-
- if (said_parse_error) {
- warning("Error while parsing SAID spec: %s", said_parse_error);
- free(said_parse_error);
- return 1;
- }
-
- if (said_tree_pos == 0) {
- warning("Out of tree space while parsing SAID spec");
- return 1;
- }
-
- if (said_blessed != 1) {
- warning("Found multiple top branches");
- return 1;
- }
-
- return 0;
-}
-
-/**********************/
-/**** Augmentation ****/
-/**********************/
-
-// primitive functions
-
-#define AUG_READ_BRANCH(a, br, p) \
- if (tree[p].type != kParseTreeBranchNode) \
- return 0; \
- a = tree[p].content.branches[br];
-
-#define AUG_READ_VALUE(a, p) \
- if (tree[p].type != kParseTreeLeafNode) \
- return 0; \
- a = tree[p].content.value;
-
-#define AUG_ASSERT(i) \
- if (!i) return 0;
-
-static int aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second) {
- // Returns the next sibling relative to the specified position in 'tree',
- // sets *first and *second to its augment node values, returns the new position
- // or 0 if there was no next sibling
- int seek, valpos;
-
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(seek, 0, pos);
- AUG_ASSERT(seek);
-
- // Now retrieve first value
- AUG_READ_BRANCH(valpos, 0, seek);
- AUG_ASSERT(valpos);
- AUG_READ_VALUE(*first, valpos);
-
- // Get second value
- AUG_READ_BRANCH(seek, 1, seek);
- AUG_ASSERT(seek);
- AUG_READ_BRANCH(valpos, 0, seek);
- AUG_ASSERT(valpos);
- AUG_READ_VALUE(*second, valpos);
-
- return pos;
-}
-
-static int aug_get_wgroup(parse_tree_node_t *tree, int pos) {
- // Returns 0 if pos in tree is not the root of a 3-element list, otherwise
- // it returns the last element (which, in practice, is the word group
- int val;
-
- AUG_READ_BRANCH(pos, 0, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
- AUG_READ_VALUE(val, pos);
-
- return val;
-}
-
-static int aug_get_base_node(parse_tree_node_t *tree) {
- int startpos = 0;
- AUG_READ_BRANCH(startpos, 1, startpos);
-
- return startpos;
-}
-
-// semi-primitive functions
-
-static int aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second) {
- // like aug_get_next_sibling, except that it recurses into the tree and
- // finds the first child (usually *not* Ayanami Rei) of the current branch
- // rather than its next sibling.
- AUG_READ_BRANCH(pos, 0, pos);
- AUG_ASSERT(pos);
- AUG_READ_BRANCH(pos, 1, pos);
- AUG_ASSERT(pos);
-
- return aug_get_next_sibling(tree, pos, first, second);
-}
-
-static void aug_find_words_recursively(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr,
- int *ref_words, int *ref_words_nr, int maxwords, int refbranch) {
- // Finds and lists all base (141) and reference (144) words */
- int major, minor;
- int word;
- int pos = aug_get_first_child(tree, startpos, &major, &minor);
-
- //if (major == WORD_TYPE_REF)
- // refbranch = 1;
-
- while (pos) {
- if ((word = aug_get_wgroup(tree, pos))) { // found a word
- if (!refbranch && major == WORD_TYPE_BASE) {
- if ((*base_words_nr) == maxwords) {
- warning("Out of regular words");
- return; // return gracefully
- }
-
- base_words[*base_words_nr] = word; // register word
- ++(*base_words_nr);
-
- }
- if (major == WORD_TYPE_REF || refbranch) {
- if ((*ref_words_nr) == maxwords) {
- warning("Out of reference words");
- return; // return gracefully
- }
-
- ref_words[*ref_words_nr] = word; // register word
- ++(*ref_words_nr);
-
- }
- if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF)
- warning("aug_find_words_recursively(): Unknown word type %03x", major);
-
- } else // Did NOT find a word group: Attempt to recurse
- aug_find_words_recursively(tree, pos, base_words, base_words_nr,
- ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF);
-
- pos = aug_get_next_sibling(tree, pos, &major, &minor);
- }
-}
-
-
-static void aug_find_words(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr,
- int *ref_words, int *ref_words_nr, int maxwords) {
- // initializing wrapper for aug_find_words_recursively()
- *base_words_nr = 0;
- *ref_words_nr = 0;
-
- aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0);
-}
-
-
-static int aug_contains_word(int *list, int length, int word) {
- int i;
-
- if (word == ANYWORD)
- return (length);
-
- for (i = 0; i < length; i++)
- if (list[i] == word)
- return 1;
-
- return 0;
-}
-
-
-static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset,
- int parse_branch, int major, int minor, int *base_words, int base_words_nr,
- int *ref_words, int ref_words_nr);
-
-static int augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset,
- int parse_basepos, int major, int minor,
- int *base_words, int base_words_nr, int *ref_words, int ref_words_nr) {
- int cmajor, cminor, cpos;
- cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor);
- if (!cpos) {
- warning("augment_match_expression_p(): Empty condition");
- return 1;
- }
-
- scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor);
-
- if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE))
- return augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr);
-
- switch (major) {
-
- case WORD_TYPE_BASE:
- while (cpos) {
- if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
- int word = aug_get_wgroup(saidt, cpos);
- scidprintf("Looking for word %03x\n", word);
-
- if (aug_contains_word(base_words, base_words_nr, word))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
- if (augment_sentence_expression(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
- int gc_major, gc_minor;
- int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
-
- while (gchild) {
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major,
- minor, base_words, base_words_nr,
- ref_words, ref_words_nr))
- return 1;
- gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
- }
- } else
- warning("augment_match_expression_p(): Unknown type 141 minor number %3x", cminor);
-
- cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
-
- }
- break;
-
- case WORD_TYPE_REF:
- while (cpos) {
- if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
- int word = aug_get_wgroup(saidt, cpos);
- scidprintf("Looking for refword %03x\n", word);
-
- if (aug_contains_word(ref_words, ref_words_nr, word))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr))
- return 1;
- } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
- int gc_major, gc_minor;
- int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
-
- while (gchild) {
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major,
- minor, base_words, base_words_nr,
- ref_words, ref_words_nr))
- return 1;
- gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
- }
- } else
- warning("augment_match_expression_p(): Unknown type 144 minor number %3x", cminor);
-
- cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
-
- }
- break;
-
- case AUGMENT_SENTENCE_PART_BRACKETS:
- if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor,
- base_words, base_words_nr, ref_words, ref_words_nr))
- return 1;
-
- scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor);
-
- switch (cmajor) {
- case WORD_TYPE_BASE:
- if (!base_words_nr)
- return 1;
- break;
-
- case WORD_TYPE_REF:
- if (!ref_words_nr)
- return 1;
- break;
-
- default:
- warning("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x", cmajor);
- }
-
- break;
-
- default:
- warning("augment_match_expression_p(): Unknown predicate %03x", major);
-
- }
-
- scidprintf("augment_match_expression_p(): Generic failure\n");
-
- return 0;
-}
-
-static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset,
- int parse_branch, int major, int minor, int *base_words, int base_words_nr,
- int *ref_words, int ref_words_nr) {
- int check_major, check_minor;
- int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor);
- do {
- if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch, check_major, check_minor,
- base_words, base_words_nr, ref_words, ref_words_nr)))
- return 0;
- } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor)));
-
- return 1;
-}
-
-static int augment_sentence_part(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, int parse_basepos, int major, int minor) {
- int pmajor, pminor;
- int parse_branch = parse_basepos;
- int optional = 0;
- int foundwords = 0;
-
- scidprintf("Augmenting (%03x %03x\n", major, minor);
-
- if (major == AUGMENT_SENTENCE_PART_BRACKETS) { // '[/ foo]' is true if '/foo' or if there
- // exists no x for which '/x' is true
- if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) {
- scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor);
- optional = 1;
- } else {
- scidprintf("Matched empty optional expression\n");
- return 1;
- }
- }
-
- if ((major < 0x141) || (major > 0x143)) {
- scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major);
- return 0;
- }
-
- while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor))) {
- if (pmajor == major) { // found matching sentence part
- int success;
- int base_words_nr;
- int ref_words_nr;
- int base_words[AUGMENT_MAX_WORDS];
- int ref_words[AUGMENT_MAX_WORDS];
-#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
- int i;
-#endif
-
- scidprintf("Found match with pminor = %03x\n", pminor);
- aug_find_words(parset, parse_branch, base_words, &base_words_nr, ref_words, &ref_words_nr, AUGMENT_MAX_WORDS);
- foundwords |= (ref_words_nr | base_words_nr);
-#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
- printf("%d base words:", base_words_nr);
- for (i = 0; i < base_words_nr; i++)
- printf(" %03x", base_words[i]);
- printf("\n%d reference words:", ref_words_nr);
- for (i = 0; i < ref_words_nr; i++)
- printf(" %03x", ref_words[i]);
- printf("\n");
-#endif
-
- success = augment_sentence_expression(saidt, augment_pos, parset, parse_basepos, major, minor,
- base_words, base_words_nr, ref_words, ref_words_nr);
-
- if (success) {
- scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor);
- return 1;
- }
- }
- }
-
- if (optional && (foundwords == 0)) {
- scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor);
- return 1;
- }
- scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor);
-
- return 0;
-}
-
-static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt) {
- int augment_basepos = 0;
- int parse_basepos;
- int major, minor;
- int dontclaim = 0;
-
- parse_basepos = aug_get_base_node(parset);
- if (!parse_basepos) {
- warning("augment_parse_nodes(): Parse tree is corrupt");
- return 0;
- }
-
- augment_basepos = aug_get_base_node(saidt);
- if (!augment_basepos) {
- warning("augment_parse_nodes(): Said tree is corrupt");
- return 0;
- }
-
- while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) {
- if ((major == 0x14b) && (minor == SAID_LONG(SAID_GT)))
- dontclaim = 1; // special case
- else // normal sentence part
- if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) {
- scidprintf("Returning failure\n");
- return 0; // fail
- }
- }
-
- scidprintf("Returning success with dontclaim=%d\n", dontclaim);
-
- if (dontclaim)
- return SAID_PARTIAL_MATCH;
- else
- return 1; // full match
-}
-
-
-/*******************/
-/**** Main code ****/
-/*******************/
-
-int said(EngineState *s, byte *spec, bool verbose) {
- int retval;
-
- parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes;
-
- if (s->_voc->parserIsValid) {
- if (said_parse_spec(spec)) {
- printf("Offending spec was: ");
- s->_voc->decipherSaidBlock(spec);
- return SAID_NO_MATCH;
- }
-
- if (verbose)
- vocab_dump_parse_tree("Said-tree", said_tree); // Nothing better to do yet
- retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0]));
-
- if (!retval)
- return SAID_NO_MATCH;
- else if (retval != SAID_PARTIAL_MATCH)
- return SAID_FULL_MATCH;
- else
- return SAID_PARTIAL_MATCH;
- }
-
- return SAID_NO_MATCH;
-}
-
-
-#ifdef SAID_DEBUG_PROGRAM
-int main (int argc, char *argv) {
- byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff};
- EngineState s;
-
- s.parser_valid = 1;
- said(&s, block);
-}
-#endif
-
-} // End of namespace Sci
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 00448f5d51..20436d5b30 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -33,71 +33,34 @@
namespace Sci {
-#if 0
-
-#define VOCAB_RESOURCE_CLASSES 996
-/**
- * Vocabulary class names.
- * These strange names were taken from an SCI01 interpreter.
- */
-const char *class_names[] = {"",
- "",
- "conj", // conjunction
- "ass", // ?
- "pos", // preposition ?
- "art", // article
- "adj", // adjective
- "pron", // pronoun
- "noun", // noun
- "auxv", // auxillary verb
- "adv", // adverb
- "verb", // verb
- "",
- "",
- "",
- ""
- };
-
-int *vocab_get_classes(ResourceManager *resMan, int* count) {
- Resource* r;
- int *c;
- unsigned int i;
-
- if ((r = resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_CLASSES), 0)) == NULL)
- return 0;
-
- c = (int *)malloc(sizeof(int) * r->size / 2);
- for (i = 2; i < r->size; i += 4) {
- c[i/4] = READ_LE_UINT16(r->data + i);
- }
- *count = r->size / 4;
-
- return c;
-}
-
-int vocab_get_class_count(ResourceManager *resMan) {
- Resource* r;
-
- if ((r = resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_CLASSES), 0)) == 0)
- return 0;
-
- return r->size / 4;
-}
-
-#endif
-
-Vocabulary::Vocabulary(ResourceManager *resMan) : _resMan(resMan) {
+Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan), _foreign(foreign) {
_parserRules = NULL;
- _vocabVersion = kVocabularySCI0;
memset(_parserNodes, 0, sizeof(_parserNodes));
// Mark parse tree as unused
_parserNodes[0].type = kParseTreeLeafNode;
- _parserNodes[0].content.value = 0;
+ _parserNodes[0].value = 0;
_synonyms.clear(); // No synonyms
debug(2, "Initializing vocabulary");
+ if (_resMan->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_MAIN_VOCAB))) {
+ _vocabVersion = kVocabularySCI0;
+ _resourceIdWords = VOCAB_RESOURCE_SCI0_MAIN_VOCAB;
+ _resourceIdSuffixes = VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB;
+ _resourceIdBranches = VOCAB_RESOURCE_SCI0_PARSE_TREE_BRANCHES;
+ } else {
+ _vocabVersion = kVocabularySCI1;
+ _resourceIdWords = VOCAB_RESOURCE_SCI1_MAIN_VOCAB;
+ _resourceIdSuffixes = VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB;
+ _resourceIdBranches = VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES;
+ }
+
+ if (_foreign) {
+ _resourceIdWords += 10;
+ _resourceIdSuffixes += 10;
+ _resourceIdBranches += 10;
+ }
if (getSciVersion() <= SCI_VERSION_1_EGA && loadParserWords()) {
loadSuffixes();
@@ -119,27 +82,46 @@ Vocabulary::~Vocabulary() {
freeSuffixes();
}
-bool Vocabulary::loadParserWords() {
+void Vocabulary::reset() {
+ parserIsValid = false; // Invalidate parser
+ parser_event = NULL_REG; // Invalidate parser event
+ parser_base = make_reg(g_sci->getEngineState()->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE);
+}
- char currentword[256] = ""; // They're not going to use words longer than 255 ;-)
- int currentwordpos = 0;
+bool Vocabulary::loadParserWords() {
+ char currentWord[VOCAB_MAX_WORDLENGTH] = "";
+ int currentWordPos = 0;
// First try to load the SCI0 vocab resource.
- Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_MAIN_VOCAB), 0);
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdWords), 0);
if (!resource) {
- warning("SCI0: Could not find a main vocabulary, trying SCI01");
- resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_MAIN_VOCAB), 0);
- _vocabVersion = kVocabularySCI1;
+ warning("Could not find a main vocabulary");
+ return false; // NOT critical: SCI1 games and some demos don't have one!
}
- if (!resource) {
- warning("SCI1: Could not find a main vocabulary");
- return false; // NOT critical: SCI1 games and some demos don't have one!
+ VocabularyVersions resourceType = _vocabVersion;
+
+ if (resourceType == kVocabularySCI0) {
+ if (resource->size < 26 * 2) {
+ warning("Invalid main vocabulary encountered: Much too small");
+ return false;
+ }
+ // Check the alphabet-offset table for any content
+ int alphabetNr;
+ for (alphabetNr = 0; alphabetNr < 26; alphabetNr++) {
+ if (READ_LE_UINT16(resource->data + alphabetNr * 2))
+ break;
+ }
+ // If all of them were empty, we are definitely seeing SCI01 vocab in disguise (e.g. pq2 japanese)
+ if (alphabetNr == 26) {
+ warning("SCI0: Found SCI01 vocabulary in disguise");
+ resourceType = kVocabularySCI1;
+ }
}
unsigned int seeker;
- if (_vocabVersion == kVocabularySCI1)
+ if (resourceType == kVocabularySCI1)
seeker = 255 * 2; // vocab.900 starts with 255 16-bit pointers which we don't use
else
seeker = 26 * 2; // vocab.000 starts with 26 16-bit pointers which we don't use
@@ -155,13 +137,13 @@ bool Vocabulary::loadParserWords() {
while (seeker < resource->size) {
byte c;
- currentwordpos = resource->data[seeker++]; // Parts of previous words may be re-used
+ currentWordPos = resource->data[seeker++]; // Parts of previous words may be re-used
- if (_vocabVersion == kVocabularySCI1) {
+ if (resourceType == kVocabularySCI1) {
c = 1;
- while (seeker < resource->size && currentwordpos < 255 && c) {
+ while (seeker < resource->size && currentWordPos < 255 && c) {
c = resource->data[seeker++];
- currentword[currentwordpos++] = c;
+ currentWord[currentWordPos++] = c;
}
if (seeker == resource->size) {
warning("SCI1: Vocabulary not usable, disabling");
@@ -171,11 +153,11 @@ bool Vocabulary::loadParserWords() {
} else {
do {
c = resource->data[seeker++];
- currentword[currentwordpos++] = c & 0x7f; // 0x80 is used to terminate the string
+ currentWord[currentWordPos++] = c & 0x7f; // 0x80 is used to terminate the string
} while (c < 0x80);
}
- currentword[currentwordpos] = 0;
+ currentWord[currentWordPos] = 0;
// Now decode class and group:
c = resource->data[seeker + 1];
@@ -184,7 +166,7 @@ bool Vocabulary::loadParserWords() {
newWord._group = (resource->data[seeker + 2]) | ((c & 0x0f) << 8);
// Add the word to the list
- _parserWords[currentword] = newWord;
+ _parserWords[currentWord] = newWord;
seeker += 3;
}
@@ -195,23 +177,20 @@ bool Vocabulary::loadParserWords() {
const char *Vocabulary::getAnyWordFromGroup(int group) {
if (group == VOCAB_MAGIC_NUMBER_GROUP)
return "{number}";
+ if (group == VOCAB_MAGIC_NOTHING_GROUP)
+ return "{nothing}";
- for (WordMap::const_iterator i = _parserWords.begin(); i != _parserWords.end(); ++i)
+ for (WordMap::const_iterator i = _parserWords.begin(); i != _parserWords.end(); ++i) {
if (i->_value._group == group)
return i->_key.c_str();
+ }
return "{invalid}";
}
bool Vocabulary::loadSuffixes() {
// Determine if we can find a SCI1 suffix vocabulary first
- Resource* resource = NULL;
-
- if (_vocabVersion == kVocabularySCI0)
- resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB), 1);
- else
- resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB), 1);
-
+ Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), 1);
if (!resource)
return false; // No vocabulary found
@@ -224,7 +203,7 @@ bool Vocabulary::loadSuffixes() {
suffix.alt_suffix_length = strlen(suffix.alt_suffix);
seeker += suffix.alt_suffix_length + 1; // Hit end of string
- suffix.class_mask = (int16)READ_BE_UINT16(resource->data + seeker);
+ suffix.result_class = (int16)READ_BE_UINT16(resource->data + seeker);
seeker += 2;
// Beginning of next string - skip leading '*'
@@ -234,7 +213,7 @@ bool Vocabulary::loadSuffixes() {
suffix.word_suffix_length = strlen(suffix.word_suffix);
seeker += suffix.word_suffix_length + 1;
- suffix.result_class = (int16)READ_BE_UINT16(resource->data + seeker);
+ suffix.class_mask = (int16)READ_BE_UINT16(resource->data + seeker);
seeker += 3; // Next entry
_parserSuffixes.push_back(suffix);
@@ -244,13 +223,7 @@ bool Vocabulary::loadSuffixes() {
}
void Vocabulary::freeSuffixes() {
- Resource* resource = NULL;
-
- if (_vocabVersion == kVocabularySCI0)
- resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB), 0);
- else
- resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB), 0);
-
+ Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), 0);
if (resource)
_resMan->unlockResource(resource);
@@ -258,12 +231,7 @@ void Vocabulary::freeSuffixes() {
}
bool Vocabulary::loadBranches() {
- Resource *resource = NULL;
-
- if (_vocabVersion == kVocabularySCI0)
- resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_PARSE_TREE_BRANCHES), 0);
- else
- resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES), 0);
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdBranches), 0);
_parserBranches.clear();
@@ -296,7 +264,7 @@ bool Vocabulary::loadBranches() {
return true;
}
-
+// we assume that *word points to an already lowercased word
ResultWord Vocabulary::lookupWord(const char *word, int word_len) {
Common::String tempword(word, word_len);
@@ -323,7 +291,7 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) {
int suff_index = word_len - suffix->alt_suffix_length;
// Offset of the start of the suffix
- if (scumm_strnicmp(suffix->alt_suffix, word + suff_index, suffix->alt_suffix_length) == 0) { // Suffix matched!
+ if (strncmp(suffix->alt_suffix, word + suff_index, suffix->alt_suffix_length) == 0) { // Suffix matched!
// Terminate word at suffix start position...:
Common::String tempword2(word, MIN(word_len, suff_index));
@@ -353,82 +321,109 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) {
return retval;
}
-void Vocabulary::decipherSaidBlock(byte *addr) {
- byte nextitem;
+void Vocabulary::debugDecipherSaidBlock(const byte *addr) {
+ bool first = true;
+ uint16 nextItem;
do {
- nextitem = *addr++;
-
- if (nextitem < 0xf0) {
- nextitem = nextitem << 8 | *addr++;
- printf(" %s[%03x]", getAnyWordFromGroup(nextitem), nextitem);
-
- nextitem = 42; // Make sure that group 0xff doesn't abort
- } else switch (nextitem) {
- case 0xf0:
- printf(" ,");
- break;
- case 0xf1:
- printf(" &");
- break;
- case 0xf2:
- printf(" /");
- break;
- case 0xf3:
- printf(" (");
- break;
- case 0xf4:
- printf(" )");
- break;
- case 0xf5:
- printf(" [");
- break;
- case 0xf6:
- printf(" ]");
- break;
- case 0xf7:
- printf(" #");
- break;
- case 0xf8:
- printf(" <");
- break;
- case 0xf9:
- printf(" >");
- break;
- case 0xff:
- break;
+ nextItem = *addr++;
+ if (nextItem != 0xff) {
+ if ((!first) && (nextItem != 0xf0))
+ printf(" ");
+ first = false;
+
+ if (nextItem < 0xf0) {
+ nextItem = nextItem << 8 | *addr++;
+ printf("%s{%03x}", getAnyWordFromGroup(nextItem), nextItem);
+
+ nextItem = 0; // Make sure that group 0xff doesn't abort
+ } else switch (nextItem) {
+ case 0xf0:
+ printf(",");
+ break;
+ case 0xf1:
+ printf("&");
+ break;
+ case 0xf2:
+ printf("/");
+ break;
+ case 0xf3:
+ printf("(");
+ break;
+ case 0xf4:
+ printf(")");
+ break;
+ case 0xf5:
+ printf("[");
+ break;
+ case 0xf6:
+ printf("]");
+ break;
+ case 0xf7:
+ printf("#");
+ break;
+ case 0xf8:
+ printf("<");
+ break;
+ case 0xf9:
+ printf(">");
+ break;
+ case 0xff:
+ break;
}
- } while (nextitem != 0xff);
-
- printf("\n");
+ }
+ } while (nextItem != 0xff);
}
+static const byte lowerCaseMap[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // 0x00
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // 0x10
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, // 0x20
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // 0x30
+ 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', // 0x40
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, // 0x50
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, // 0x60
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, // 0x70
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x84, 0x86, // 0x80
+ //^^ ^^^^ ^^^^
+ 0x82, 0x91, 0x91, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x94, 0x81, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // 0x90
+ //^^ ^^^^ ^^^^ ^^^^
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // 0xa0
+ // ^^^^
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, // 0xb0
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, // 0xc0
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, // 0xd0
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, // 0xe0
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff // 0xf0
+};
+
bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, char **error) {
- const char *lastword = sentence;
+ char currentWord[VOCAB_MAX_WORDLENGTH] = "";
int pos_in_sentence = 0;
- char c;
- int wordlen = 0;
+ unsigned char c;
+ int wordLen = 0;
*error = NULL;
do {
c = sentence[pos_in_sentence++];
-
- if (isalnum(c) || (c == '-' && wordlen))
- ++wordlen;
+ if (isalnum(c) || (c == '-' && wordLen) || (c >= 0x80)) {
+ currentWord[wordLen] = lowerCaseMap[c];
+ ++wordLen;
+ }
// Continue on this word */
// Words may contain a '-', but may not
// start with one.
else {
- if (wordlen) { // Finished a word?
+ if (wordLen) { // Finished a word?
- ResultWord lookup_result = lookupWord(lastword, wordlen);
+ ResultWord lookup_result = lookupWord(currentWord, wordLen);
// Look it up
if (lookup_result._class == -1) { // Not found?
- *error = (char *)calloc(wordlen + 1, 1);
- strncpy(*error, lastword, wordlen); // Set the offending word
+ *error = (char *)calloc(wordLen + 1, 1);
+ strncpy(*error, currentWord, wordLen); // Set the offending word
retval.clear();
return false; // And return with error
}
@@ -437,8 +432,7 @@ bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, ch
retval.push_back(lookup_result);
}
- lastword = sentence + pos_in_sentence;
- wordlen = 0;
+ wordLen = 0;
}
} while (c); // Until terminator is hit
@@ -447,7 +441,7 @@ bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, ch
}
void Vocabulary::printSuffixes() const {
- char word_buf[256], alt_buf[256];
+ char word_buf[VOCAB_MAX_WORDLENGTH], alt_buf[VOCAB_MAX_WORDLENGTH];
Console *con = g_sci->getSciDebugger();
int i = 0;
@@ -476,30 +470,25 @@ void Vocabulary::printParserWords() const {
con->DebugPrintf("\n");
}
-void _vocab_recursive_ptree_dump_treelike(parse_tree_node_t *nodes, int nr, int prevnr) {
- if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) {
- printf("Error(%04x)", nr);
- return;
- }
+void _vocab_recursive_ptree_dump_treelike(ParseTreeNode *tree) {
+ assert(tree);
- if (nodes[nr].type == kParseTreeLeafNode)
- //printf("[%03x]%04x", nr, nodes[nr].content.value);
- printf("%x", nodes[nr].content.value);
+ if (tree->type == kParseTreeLeafNode)
+ printf("%x", tree->value);
else {
- int lbranch = nodes[nr].content.branches[0];
- int rbranch = nodes[nr].content.branches[1];
- //printf("<[%03x]", nr);
+ ParseTreeNode* lbranch = tree->left;
+ ParseTreeNode* rbranch = tree->right;
printf("<");
if (lbranch)
- _vocab_recursive_ptree_dump_treelike(nodes, lbranch, nr);
+ _vocab_recursive_ptree_dump_treelike(lbranch);
else
printf("NULL");
printf(",");
if (rbranch)
- _vocab_recursive_ptree_dump_treelike(nodes, rbranch, nr);
+ _vocab_recursive_ptree_dump_treelike(rbranch);
else
printf("NULL");
@@ -507,55 +496,52 @@ void _vocab_recursive_ptree_dump_treelike(parse_tree_node_t *nodes, int nr, int
}
}
-void _vocab_recursive_ptree_dump(parse_tree_node_t *nodes, int nr, int prevnr, int blanks) {
- int lbranch = nodes[nr].content.branches[0];
- int rbranch = nodes[nr].content.branches[1];
- int i;
+void _vocab_recursive_ptree_dump(ParseTreeNode *tree, int blanks) {
+ assert(tree);
- if (nodes[nr].type == kParseTreeLeafNode) {
- printf("vocab_dump_parse_tree: Error: consp is nil for element %03x\n", nr);
- return;
- }
+ ParseTreeNode* lbranch = tree->left;
+ ParseTreeNode* rbranch = tree->right;
+ int i;
- if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) {
- printf("Error(%04x))", nr);
+ if (tree->type == kParseTreeLeafNode) {
+ printf("vocab_dump_parse_tree: Error: consp is nil\n");
return;
}
if (lbranch) {
- if (nodes[lbranch].type == kParseTreeBranchNode) {
+ if (lbranch->type == kParseTreeBranchNode) {
printf("\n");
for (i = 0; i < blanks; i++)
printf(" ");
printf("(");
- _vocab_recursive_ptree_dump(nodes, lbranch, nr, blanks + 1);
+ _vocab_recursive_ptree_dump(lbranch, blanks + 1);
printf(")\n");
for (i = 0; i < blanks; i++)
printf(" ");
} else
- printf("%x", nodes[lbranch].content.value);
+ printf("%x", lbranch->value);
printf(" ");
}/* else printf ("nil");*/
if (rbranch) {
- if (nodes[rbranch].type == kParseTreeBranchNode)
- _vocab_recursive_ptree_dump(nodes, rbranch, nr, blanks);
+ if (rbranch->type == kParseTreeBranchNode)
+ _vocab_recursive_ptree_dump(rbranch, blanks);
else
- printf("%x", nodes[rbranch].content.value);
+ printf("%x", rbranch->value);
}/* else printf("nil");*/
}
-void vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes) {
+void vocab_dump_parse_tree(const char *tree_name, ParseTreeNode *nodes) {
//_vocab_recursive_ptree_dump_treelike(nodes, 0, 0);
printf("(setq %s \n'(", tree_name);
- _vocab_recursive_ptree_dump(nodes, 0, 0, 1);
+ _vocab_recursive_ptree_dump(nodes, 1);
printf("))\n");
}
void Vocabulary::dumpParseTree() {
//_vocab_recursive_ptree_dump_treelike(nodes, 0, 0);
printf("(setq parse-tree \n'(");
- _vocab_recursive_ptree_dump(_parserNodes, 0, 0, 1);
+ _vocab_recursive_ptree_dump(_parserNodes, 1);
printf("))\n");
}
@@ -575,10 +561,10 @@ void Vocabulary::printParserNodes(int num) {
for (int i = 0; i < num; i++) {
con->DebugPrintf(" Node %03x: ", i);
if (_parserNodes[i].type == kParseTreeLeafNode)
- con->DebugPrintf("Leaf: %04x\n", _parserNodes[i].content.value);
+ con->DebugPrintf("Leaf: %04x\n", _parserNodes[i].value);
else
- con->DebugPrintf("Branch: ->%04x, ->%04x\n", _parserNodes[i].content.branches[0],
- _parserNodes[i].content.branches[1]);
+ con->DebugPrintf("Branch: ->%04x, ->%04x\n", _parserNodes[i].left,
+ _parserNodes[i].right);
}
}
@@ -591,7 +577,7 @@ int Vocabulary::parseNodes(int *i, int *pos, int type, int nr, int argc, const c
if (type == kParseNumber) {
_parserNodes[*pos += 1].type = kParseTreeLeafNode;
- _parserNodes[*pos].content.value = nr;
+ _parserNodes[*pos].value = nr;
return *pos;
}
if (type == kParseEndOfInput) {
@@ -623,7 +609,15 @@ int Vocabulary::parseNodes(int *i, int *pos, int type, int nr, int argc, const c
}
}
- if ((newPos = _parserNodes[oldPos].content.branches[j] = parseNodes(i, pos, nextToken, nextValue, argc, argv)) == -1)
+ newPos = parseNodes(i, pos, nextToken, nextValue, argc, argv);
+
+ if (j == 0)
+ _parserNodes[oldPos].left = &_parserNodes[newPos];
+ else
+ _parserNodes[oldPos].right = &_parserNodes[newPos];
+
+
+ if (newPos == -1)
return -1;
}
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index dccef0f5f3..d4df8af715 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -73,13 +73,16 @@ enum {
kParseNumber = 4
};
+#define VOCAB_MAX_WORDLENGTH 256
+
/* Anywords are ignored by the parser */
#define VOCAB_CLASS_ANYWORD 0xff
/* This word class is used for numbers */
#define VOCAB_MAGIC_NUMBER_GROUP 0xffd /* 0xffe ? */
+#define VOCAB_MAGIC_NOTHING_GROUP 0xffe
-/* Number of nodes for each parse_tree_node structure */
+/* Number of nodes for each ParseTreeNode structure */
#define VOCAB_TREE_NODES 500
#define VOCAB_TREE_NODE_LAST_WORD_STORAGE 0x140
@@ -115,7 +118,7 @@ struct ResultWord {
typedef Common::List<ResultWord> ResultWordList;
-typedef Common::HashMap<Common::String, ResultWord, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> WordMap;
+typedef Common::HashMap<Common::String, ResultWord, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> WordMap;
struct ParseRuleList;
@@ -149,16 +152,16 @@ struct parse_tree_branch_t {
};
enum ParseTypes {
- kParseTreeLeafNode = 0,
- kParseTreeBranchNode = 1
+ kParseTreeWordNode = 4,
+ kParseTreeLeafNode = 5,
+ kParseTreeBranchNode = 6
};
-struct parse_tree_node_t {
+struct ParseTreeNode {
ParseTypes type; /**< leaf or branch */
- union {
- int value; /**< For leaves */
- short branches[2]; /**< For branches */
- } content;
+ int value; /**< For leaves */
+ ParseTreeNode* left; /**< Left child, for branches */
+ ParseTreeNode* right; /**< Right child, for branches */
};
enum VocabularyVersions {
@@ -168,9 +171,12 @@ enum VocabularyVersions {
class Vocabulary {
public:
- Vocabulary(ResourceManager *resMan);
+ Vocabulary(ResourceManager *resMan, bool foreign);
~Vocabulary();
+ // reset parser status
+ void reset();
+
/**
* Gets any word from the specified group. For debugging only.
* @param group Group number
@@ -229,7 +235,7 @@ public:
* For debugging only.
* @param pos pointer to the data to dump
*/
- void decipherSaidBlock(byte *pos);
+ void debugDecipherSaidBlock(const byte *pos);
/**
* Prints the parser suffixes to the debug console.
@@ -301,6 +307,11 @@ private:
ResourceManager *_resMan;
VocabularyVersions _vocabVersion;
+ bool _foreign;
+ uint16 _resourceIdWords;
+ uint16 _resourceIdSuffixes;
+ uint16 _resourceIdBranches;
+
// Parser-related lists
SuffixList _parserSuffixes;
ParseRuleList *_parserRules; /**< GNF rules used in the parser algorithm */
@@ -310,7 +321,7 @@ private:
public:
// Accessed by said()
- parse_tree_node_t _parserNodes[VOCAB_TREE_NODES]; /**< The parse tree */
+ ParseTreeNode _parserNodes[VOCAB_TREE_NODES]; /**< The parse tree */
// Parser data:
reg_t parser_base; /**< Base address for the parser error reporting mechanism */
@@ -323,7 +334,7 @@ public:
* @param tree_name Name of the tree to dump (free-form)
* @param nodes The nodes containing the parse tree
*/
-void vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes);
+void vocab_dump_parse_tree(const char *tree_name, ParseTreeNode *nodes);
@@ -334,7 +345,7 @@ void vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes);
* @param verbose Whether to display the parse tree after building it
* @return 1 on a match, 0 otherwise
*/
-int said(EngineState *s, byte *spec, bool verbose);
+int said(EngineState *s, const byte *spec, bool verbose);
} // End of namespace Sci
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 4888dbd4cb..17dc7171dd 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -26,9 +26,11 @@
// Resource library
#include "common/file.h"
+#include "common/fs.h"
#include "common/macresman.h"
#include "sci/resource.h"
+#include "sci/resource_intern.h"
#include "sci/util.h"
namespace Sci {
@@ -45,18 +47,6 @@ struct resource_index_t {
uint16 wSize;
};
-struct ResourceSource {
- ResSourceType source_type;
- bool scanned;
- Common::String location_name; // FIXME: Replace by FSNode ?
- const Common::FSNode *resourceFile;
- int volume_number;
- ResourceSource *associated_map;
- uint32 audioCompressionType;
- int32 *audioCompressionOffsetMapping;
- Common::MacResManager macResMan;
-};
-
//////////////////////////////////////////////////////////////////////
static SciVersion s_sciVersion = SCI_VERSION_NONE; // FIXME: Move this inside a suitable class, e.g. SciEngine
@@ -118,32 +108,81 @@ static const char *sci_error_types[] = {
"SCI version is unsupported"
};
-// These are the 20 resource types supported by SCI1.1
-static const char *resourceTypeNames[] = {
+static const char *s_resourceTypeNames[] = {
"view", "pic", "script", "text", "sound",
"memory", "vocab", "font", "cursor",
"patch", "bitmap", "palette", "cdaudio",
"audio", "sync", "message", "map", "heap",
- "audio36", "sync36", "", "", "robot"
+ "audio36", "sync36", "xlate", "robot", "vmd",
+ "chunk", "macibin", "macibis", "macpict"
};
-static const char *resourceTypeSuffixes[] = {
+static const char *s_resourceTypeSuffixes[] = {
"v56", "p56", "scr", "tex", "snd",
- " ", "voc", "fon", "cur", "pat",
+ "", "voc", "fon", "cur", "pat",
"bit", "pal", "cda", "aud", "syn",
- "msg", "map", "hep", "aud", "syn",
- "trn", " ", "rbt"
-};
+ "msg", "map", "hep", "", "",
+ "trn", "rbt", "vmd", "chk", "",
+ "", ""
+};
const char *getResourceTypeName(ResourceType restype) {
if (restype != kResourceTypeInvalid)
- return resourceTypeNames[restype];
+ return s_resourceTypeNames[restype];
else
return "invalid";
}
+static const ResourceType s_resTypeMapSci0[] = {
+ kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeText, // 0x00-0x03
+ kResourceTypeSound, kResourceTypeMemory, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07
+ kResourceTypeCursor, kResourceTypePatch, kResourceTypeBitmap, kResourceTypePalette, // 0x08-0x0B
+ kResourceTypeCdAudio, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F
+ kResourceTypeMap, kResourceTypeHeap, kResourceTypeAudio36, kResourceTypeSync36, // 0x10-0x13
+ kResourceTypeTranslation // 0x14
+};
+
+#ifdef ENABLE_SCI32
+// TODO: 12 should be "Wave", but SCI seems to just store it in Audio resources
+static const ResourceType s_resTypeMapSci21[] = {
+ kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeText, // 0x00-0x03
+ kResourceTypeSound, kResourceTypeMemory, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07
+ kResourceTypeCursor, kResourceTypePatch, kResourceTypeBitmap, kResourceTypePalette, // 0x08-0x0B
+ kResourceTypeInvalid, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F
+ kResourceTypeMap, kResourceTypeHeap, kResourceTypeChunk, kResourceTypeAudio36, // 0x10-0x13
+ kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD // 0x14-0x17
+};
+#endif
+
+ResourceType ResourceManager::convertResType(byte type) {
+ type &= 0x7f;
+
+ if (_mapVersion != kResVersionSci32) {
+ // SCI0 - SCI2
+ if (type < ARRAYSIZE(s_resTypeMapSci0))
+ return s_resTypeMapSci0[type];
+ } else {
+ // SCI2.1+
+#ifdef ENABLE_SCI32
+ if (type < ARRAYSIZE(s_resTypeMapSci21)) {
+ // LSL6 hires doesn't have the chunk resource type, to match
+ // the resource types of the lowres version, thus we use the
+ // older resource types here
+ if (g_sci && g_sci->getGameId() == GID_LSL6HIRES)
+ return s_resTypeMapSci0[type];
+ else
+ return s_resTypeMapSci21[type];
+ }
+#else
+ error("SCI32 support not compiled in");
+#endif
+ }
+
+ return kResourceTypeInvalid;
+}
+
//-- Resource main functions --
-Resource::Resource() {
+Resource::Resource(ResourceManager *resMan, ResourceId id) : _resMan(resMan), _id(id) {
data = NULL;
size = 0;
_fileOffset = 0;
@@ -156,7 +195,7 @@ Resource::Resource() {
Resource::~Resource() {
delete[] data;
- if (_source && _source->source_type == kSourcePatch)
+ if (_source && _source->getSourceType() == kSourcePatch)
delete _source;
}
@@ -167,101 +206,76 @@ void Resource::unalloc() {
}
void Resource::writeToStream(Common::WriteStream *stream) const {
- stream->writeByte(_id.type | 0x80); // 0x80 is required by old sierra sci, otherwise it wont accept the patch file
+ stream->writeByte(getType() | 0x80); // 0x80 is required by old sierra sci, otherwise it wont accept the patch file
stream->writeByte(_headerSize);
if (_headerSize > 0)
stream->write(_header, _headerSize);
stream->write(data, size);
}
-uint32 Resource::getAudioCompressionType() {
- return _source->audioCompressionType;
+uint32 Resource::getAudioCompressionType() const {
+ return _source->getAudioCompressionType();
}
-//-- resMan helper functions --
+uint32 AudioVolumeResourceSource::getAudioCompressionType() const {
+ return _audioCompressionType;
+}
-// Resource source list management
-ResourceSource *ResourceManager::addExternalMap(const char *file_name, int volume_nr) {
- ResourceSource *newsrc = new ResourceSource();
+ResourceSource::ResourceSource(ResSourceType type, const Common::String &name, int volNum, const Common::FSNode *resFile)
+ : _sourceType(type), _name(name), _volumeNumber(volNum), _resourceFile(resFile) {
+ _scanned = false;
+}
- newsrc->source_type = kSourceExtMap;
- newsrc->location_name = file_name;
- newsrc->resourceFile = 0;
- newsrc->scanned = false;
- newsrc->associated_map = NULL;
- newsrc->volume_number = volume_nr;
+ResourceSource::~ResourceSource() {
+}
- _sources.push_back(newsrc);
- return newsrc;
+MacResourceForkResourceSource::MacResourceForkResourceSource(const Common::String &name, int volNum)
+ : ResourceSource(kSourceMacResourceFork, name, volNum) {
+ _macResMan = new Common::MacResManager();
+ assert(_macResMan);
}
-ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) {
- ResourceSource *newsrc = new ResourceSource();
+MacResourceForkResourceSource::~MacResourceForkResourceSource() {
+ delete _macResMan;
+}
+
+//-- resMan helper functions --
+
+// Resource source list management
- newsrc->source_type = kSourceExtMap;
- newsrc->location_name = mapFile->getName();
- newsrc->resourceFile = mapFile;
- newsrc->scanned = false;
- newsrc->associated_map = NULL;
- newsrc->volume_number = 0;
+ResourceSource *ResourceManager::addExternalMap(const Common::String &filename, int volume_nr) {
+ ResourceSource *newsrc = new ExtMapResourceSource(filename, volume_nr);
_sources.push_back(newsrc);
return newsrc;
}
-ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType type, const char *filename, int number) {
- ResourceSource *newsrc = new ResourceSource();
-
- newsrc->source_type = type;
- newsrc->scanned = false;
- newsrc->location_name = filename;
- newsrc->resourceFile = 0;
- newsrc->volume_number = number;
- newsrc->associated_map = map;
- newsrc->audioCompressionType = 0;
- newsrc->audioCompressionOffsetMapping = NULL;
- if (type == kSourceAudioVolume)
- checkIfAudioVolumeIsCompressed(newsrc);
+ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, int volume_nr) {
+ ResourceSource *newsrc = new ExtMapResourceSource(mapFile->getName(), volume_nr, mapFile);
_sources.push_back(newsrc);
return newsrc;
}
-ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType type, const Common::FSNode *resFile, int number) {
- ResourceSource *newsrc = new ResourceSource();
-
- newsrc->source_type = type;
- newsrc->scanned = false;
- newsrc->location_name = resFile->getName();
- newsrc->resourceFile = resFile;
- newsrc->volume_number = number;
- newsrc->associated_map = map;
- newsrc->audioCompressionType = 0;
- newsrc->audioCompressionOffsetMapping = NULL;
- if (type == kSourceAudioVolume)
- checkIfAudioVolumeIsCompressed(newsrc);
+ResourceSource *ResourceManager::addSource(ResourceSource *newsrc) {
+ assert(newsrc);
_sources.push_back(newsrc);
return newsrc;
}
-ResourceSource *ResourceManager::addPatchDir(const char *dirname) {
- ResourceSource *newsrc = new ResourceSource();
-
- newsrc->source_type = kSourceDirectory;
- newsrc->scanned = false;
- newsrc->location_name = dirname;
+ResourceSource *ResourceManager::addPatchDir(const Common::String &dirname) {
+ ResourceSource *newsrc = new DirectoryResourceSource(dirname);
_sources.push_back(newsrc);
return 0;
}
-ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) {
+ResourceSource *ResourceManager::findVolume(ResourceSource *map, int volume_nr) {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
- ResourceSource *src = *it;
- if ((src->source_type == kSourceVolume || src->source_type == kSourceAudioVolume)
- && src->associated_map == map && src->volume_number == volume_nr)
+ ResourceSource *src = (*it)->findVolume(map, volume_nr);
+ if (src)
return src;
}
@@ -270,37 +284,9 @@ ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) {
// Resource manager constructors and operations
-void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
- Common::File *file = getVolumeFile(source->location_name.c_str());
- if (!file) {
- warning("Failed to open %s", source->location_name.c_str());
- return;
- }
- file->seek(0, SEEK_SET);
- uint32 compressionType = file->readUint32BE();
- switch (compressionType) {
- case MKID_BE('MP3 '):
- case MKID_BE('OGG '):
- case MKID_BE('FLAC'):
- // Detected a compressed audio volume
- source->audioCompressionType = compressionType;
- // Now read the whole offset mapping table for later usage
- int32 recordCount = file->readUint32LE();
- if (!recordCount)
- error("compressed audio volume doesn't contain any entries!");
- int32 *offsetMapping = new int32[(recordCount + 1) * 2];
- source->audioCompressionOffsetMapping = offsetMapping;
- for (int recordNo = 0; recordNo < recordCount; recordNo++) {
- *offsetMapping++ = file->readUint32LE();
- *offsetMapping++ = file->readUint32LE();
- }
- // Put ending zero
- *offsetMapping++ = 0;
- *offsetMapping++ = file->size();
- }
-}
+bool Resource::loadPatch(Common::SeekableReadStream *file) {
+ Resource *res = this;
-bool ResourceManager::loadPatch(Resource *res, Common::File &file) {
// We assume that the resource type matches res->type
// We also assume that the current file position is right at the actual data (behind resourceid/headersize byte)
@@ -315,12 +301,12 @@ bool ResourceManager::loadPatch(Resource *res, Common::File &file) {
unsigned int really_read;
if (res->_headerSize > 0) {
- really_read = file.read(res->_header, res->_headerSize);
+ really_read = file->read(res->_header, res->_headerSize);
if (really_read != res->_headerSize)
error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->_headerSize);
}
- really_read = file.read(res->data, res->size);
+ really_read = file->read(res->data, res->size);
if (really_read != res->size)
error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
@@ -328,87 +314,28 @@ bool ResourceManager::loadPatch(Resource *res, Common::File &file) {
return true;
}
-bool ResourceManager::loadFromPatchFile(Resource *res) {
+bool Resource::loadFromPatchFile() {
Common::File file;
- const char *filename = res->_source->location_name.c_str();
+ const Common::String &filename = _source->getLocationName();
if (file.open(filename) == false) {
- warning("Failed to open patch file %s", filename);
- res->unalloc();
+ warning("Failed to open patch file %s", filename.c_str());
+ unalloc();
return false;
}
// Skip resourceid and header size byte
file.seek(2, SEEK_SET);
- return loadPatch(res, file);
-}
-
-bool ResourceManager::loadFromWaveFile(Resource *res, Common::File &file) {
- res->data = new byte[res->size];
-
- uint32 really_read = file.read(res->data, res->size);
- if (really_read != res->size)
- error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
-
- res->_status = kResStatusAllocated;
- return true;
+ return loadPatch(&file);
}
-bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::File &file) {
- // Check for WAVE files here
- uint32 riffTag = file.readUint32BE();
- if (riffTag == MKID_BE('RIFF')) {
- res->_headerSize = 0;
- res->size = file.readUint32LE();
- file.seek(-8, SEEK_CUR);
- return loadFromWaveFile(res, file);
- }
- file.seek(-4, SEEK_CUR);
-
- ResourceType type = (ResourceType)(file.readByte() & 0x7f);
- if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio))
- || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) {
- warning("Resource type mismatch loading %s from %s", res->_id.toString().c_str(), file.getName());
- res->unalloc();
- return false;
- }
-
- res->_headerSize = file.readByte();
-
- if (type == kResourceTypeAudio) {
- if (res->_headerSize != 11 && res->_headerSize != 12) {
- warning("Unsupported audio header");
- res->unalloc();
- return false;
- }
-
- // Load sample size
- file.seek(7, SEEK_CUR);
- res->size = file.readUint32LE();
- // Adjust offset to point at the header data again
- file.seek(-11, SEEK_CUR);
- }
-
- return loadPatch(res, file);
-}
-
-bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::File &file) {
- res->data = new byte[res->size];
-
- if (res->data == NULL) {
- error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str());
- }
-
- unsigned int really_read = file.read(res->data, res->size);
- if (really_read != res->size)
- warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
-
- res->_status = kResStatusAllocated;
- return true;
-}
-
-Common::File *ResourceManager::getVolumeFile(const char *filename) {
+Common::SeekableReadStream *ResourceManager::getVolumeFile(ResourceSource *source) {
Common::List<Common::File *>::iterator it = _volumeFiles.begin();
Common::File *file;
+ if (source->_resourceFile)
+ return source->_resourceFile->createReadStream();
+
+ const char *filename = source->getLocationName().c_str();
+
// check if file is already opened
while (it != _volumeFiles.end()) {
file = *it;
@@ -441,161 +368,182 @@ Common::File *ResourceManager::getVolumeFile(const char *filename) {
static uint32 resTypeToMacTag(ResourceType type);
void ResourceManager::loadResource(Resource *res) {
- if (res->_source->source_type == kSourcePatch && loadFromPatchFile(res))
- return;
+ res->_source->loadResource(this, res);
+}
+
+
+void PatchResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ bool result = res->loadFromPatchFile();
+ if (!result) {
+ // TODO: We used to fallback to the "default" code here if loadFromPatchFile
+ // failed, but I am not sure whether that is really appropriate.
+ // In fact it looks like a bug to me, so I commented this out for now.
+ //ResourceSource::loadResource(res);
+ }
+}
- if (res->_source->source_type == kSourceMacResourceFork) {
- //error("ResourceManager::loadResource(): TODO: Mac resource fork ;)");
- Common::SeekableReadStream *stream = res->_source->macResMan.getResource(resTypeToMacTag(res->_id.type), res->_id.number);
+void MacResourceForkResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *stream = _macResMan->getResource(resTypeToMacTag(res->getType()), res->getNumber());
- if (!stream)
- error("Could not get Mac resource fork resource");
+ if (!stream)
+ error("Could not get Mac resource fork resource: %s %d", getResourceTypeName(res->getType()), res->getNumber());
- int error = decompress(res, stream);
- if (error) {
- warning("Error %d occured while reading %s from Mac resource file: %s",
- error, res->_id.toString().c_str(), sci_error_types[error]);
+ int error = res->decompress(resMan->getVolVersion(), stream);
+ if (error) {
+ warning("Error %d occurred while reading %s from Mac resource file: %s",
+ error, res->_id.toString().c_str(), sci_error_types[error]);
+ res->unalloc();
+ }
+}
+
+Common::SeekableReadStream *ResourceSource::getVolumeFile(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = resMan->getVolumeFile(this);
+
+ if (!fileStream) {
+ warning("Failed to open %s", getLocationName().c_str());
+ if (res)
res->unalloc();
- }
- return;
}
- Common::File *file;
- // Either loading from volume or patch loading failed
- file = getVolumeFile(res->_source->location_name.c_str());
- if (!file) {
- warning("Failed to open %s", res->_source->location_name.c_str());
- res->unalloc();
+ return fileStream;
+}
+
+void WaveResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, res);
+ if (!fileStream)
return;
- }
- switch(res->_source->source_type) {
- case kSourceWave:
- file->seek(res->_fileOffset, SEEK_SET);
- loadFromWaveFile(res, *file);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ res->loadFromWaveFile(fileStream);
+ if (_resourceFile)
+ delete fileStream;
+}
+
+void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, res);
+ if (!fileStream)
return;
- case kSourceAudioVolume:
- if (res->_source->audioCompressionType) {
- // this file is compressed, so lookup our offset in the offset-translation table and get the new offset
- // also calculate the compressed size by using the next offset
- int32 *mappingTable = res->_source->audioCompressionOffsetMapping;
- int32 compressedOffset = 0;
-
- do {
- if (*mappingTable == res->_fileOffset) {
- mappingTable++;
- compressedOffset = *mappingTable;
- // Go to next compressed offset and use that to calculate size of compressed sample
- switch (res->_id.type) {
- case kResourceTypeSync:
- case kResourceTypeSync36:
- // we should already have a (valid) size
- break;
- default:
- mappingTable += 2;
- res->size = *mappingTable - compressedOffset;
- }
+ if (_audioCompressionType) {
+ // this file is compressed, so lookup our offset in the offset-translation table and get the new offset
+ // also calculate the compressed size by using the next offset
+ int32 *mappingTable = _audioCompressionOffsetMapping;
+ int32 compressedOffset = 0;
+
+ do {
+ if (*mappingTable == res->_fileOffset) {
+ mappingTable++;
+ compressedOffset = *mappingTable;
+ // Go to next compressed offset and use that to calculate size of compressed sample
+ switch (res->getType()) {
+ case kResourceTypeSync:
+ case kResourceTypeSync36:
+ // we should already have a (valid) size
break;
+ default:
+ mappingTable += 2;
+ res->size = *mappingTable - compressedOffset;
}
- mappingTable += 2;
- } while (*mappingTable);
-
- if (!compressedOffset)
- error("could not translate offset to compressed offset in audio volume");
- file->seek(compressedOffset, SEEK_SET);
-
- switch (res->_id.type) {
- case kResourceTypeAudio:
- case kResourceTypeAudio36:
- // Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1
- loadFromAudioVolumeSCI1(res, *file);
- return;
- default:
break;
}
- } else {
- // original file, directly seek to given offset and get SCI1/SCI1.1 audio resource
- file->seek(res->_fileOffset, SEEK_SET);
+ mappingTable += 2;
+ } while (*mappingTable);
+
+ if (!compressedOffset)
+ error("could not translate offset to compressed offset in audio volume");
+ fileStream->seek(compressedOffset, SEEK_SET);
+
+ switch (res->getType()) {
+ case kResourceTypeAudio:
+ case kResourceTypeAudio36:
+ // Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1
+ res->loadFromAudioVolumeSCI1(fileStream);
+ if (_resourceFile)
+ delete fileStream;
+ return;
+ default:
+ break;
}
- if (getSciVersion() < SCI_VERSION_1_1)
- loadFromAudioVolumeSCI1(res, *file);
- else
- loadFromAudioVolumeSCI11(res, *file);
+ } else {
+ // original file, directly seek to given offset and get SCI1/SCI1.1 audio resource
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ }
+ if (getSciVersion() < SCI_VERSION_1_1)
+ res->loadFromAudioVolumeSCI1(fileStream);
+ else
+ res->loadFromAudioVolumeSCI11(fileStream);
+
+ if (_resourceFile)
+ delete fileStream;
+}
+
+void ResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, res);
+ if (!fileStream)
return;
- default:
- file->seek(res->_fileOffset, SEEK_SET);
- int error = decompress(res, file);
- if (error) {
- warning("Error %d occured while reading %s from resource file: %s",
- error, res->_id.toString().c_str(), sci_error_types[error]);
- res->unalloc();
- }
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+
+ int error = res->decompress(resMan->getVolVersion(), fileStream);
+ if (error) {
+ warning("Error %d occurred while reading %s from resource file: %s",
+ error, res->_id.toString().c_str(), sci_error_types[error]);
+ res->unalloc();
}
+
+ if (_resourceFile)
+ delete fileStream;
}
Resource *ResourceManager::testResource(ResourceId id) {
return _resMap.getVal(id, NULL);
}
-int sci0_get_compression_method(Common::ReadStream &stream) {
- uint16 compressionMethod;
-
- stream.readUint16LE();
- stream.readUint16LE();
- stream.readUint16LE();
- compressionMethod = stream.readUint16LE();
- if (stream.err())
- return SCI_ERROR_IO_ERROR;
-
- return compressionMethod;
-}
-
int ResourceManager::addAppropriateSources() {
Common::ArchiveMemberList files;
- if (Common::File::exists("RESOURCE.MAP")) {
+ if (Common::File::exists("resource.map")) {
// SCI0-SCI2 file naming scheme
- ResourceSource *map = addExternalMap("RESOURCE.MAP");
+ ResourceSource *map = addExternalMap("resource.map");
- SearchMan.listMatchingMembers(files, "RESOURCE.0??");
+ SearchMan.listMatchingMembers(files, "resource.0??");
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
const Common::String name = (*x)->getName();
const char *dot = strrchr(name.c_str(), '.');
int number = atoi(dot + 1);
- addSource(map, kSourceVolume, name.c_str(), number);
+ addSource(new VolumeResourceSource(name, map, number));
}
#ifdef ENABLE_SCI32
// GK1CD hires content
- if (Common::File::exists("ALT.MAP") && Common::File::exists("RESOURCE.ALT"))
- addSource(addExternalMap("ALT.MAP", 10), kSourceVolume, "RESOURCE.ALT", 10);
+ if (Common::File::exists("alt.map") && Common::File::exists("resource.alt"))
+ addSource(new VolumeResourceSource("resource.alt", addExternalMap("alt.map", 10), 10));
#endif
} else if (Common::File::exists("Data1")) {
// Mac SCI1.1+ file naming scheme
- SearchMan.listMatchingMembers(files, "Data?");
+ SearchMan.listMatchingMembers(files, "Data?*");
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
Common::String filename = (*x)->getName();
- addSource(0, kSourceMacResourceFork, filename.c_str(), atoi(filename.c_str() + 4));
+ addSource(new MacResourceForkResourceSource(filename, atoi(filename.c_str() + 4)));
}
#ifdef ENABLE_SCI32
// Mac SCI32 games have extra folders for patches
addPatchDir("Robot Folder");
addPatchDir("Sound Folder");
addPatchDir("Voices Folder");
+ addPatchDir("Voices");
//addPatchDir("VMD Folder");
// There can also be a "Patches" resource fork with patches
if (Common::File::exists("Patches"))
- addSource(0, kSourceMacResourceFork, "Patches", 100);
+ addSource(new MacResourceForkResourceSource("Patches", 100));
} else {
// SCI2.1-SCI3 file naming scheme
Common::ArchiveMemberList mapFiles;
- SearchMan.listMatchingMembers(mapFiles, "RESMAP.0??");
- SearchMan.listMatchingMembers(files, "RESSCI.0??");
+ SearchMan.listMatchingMembers(mapFiles, "resmap.0??");
+ SearchMan.listMatchingMembers(files, "ressci.0??");
// We need to have the same number of maps as resource archives
if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size())
@@ -610,16 +558,16 @@ int ResourceManager::addAppropriateSources() {
int resNumber = atoi(strrchr(resName.c_str(), '.') + 1);
if (mapNumber == resNumber) {
- addSource(addExternalMap(mapName.c_str(), mapNumber), kSourceVolume, resName.c_str(), mapNumber);
+ addSource(new VolumeResourceSource(resName, addExternalMap(mapName, mapNumber), mapNumber));
break;
}
}
}
// SCI2.1 resource patches
- if (Common::File::exists("RESMAP.PAT") && Common::File::exists("RESSCI.PAT")) {
+ if (Common::File::exists("resmap.pat") && Common::File::exists("ressci.pat")) {
// We add this resource with a map which surely won't exist
- addSource(addExternalMap("RESMAP.PAT", 100), kSourceVolume, "RESSCI.PAT", 100);
+ addSource(new VolumeResourceSource("ressci.pat", addExternalMap("resmap.pat", 100), 100));
}
}
#else
@@ -628,14 +576,22 @@ int ResourceManager::addAppropriateSources() {
#endif
addPatchDir(".");
- if (Common::File::exists("MESSAGE.MAP"))
- addSource(addExternalMap("MESSAGE.MAP"), kSourceVolume, "RESOURCE.MSG", 0);
+
+ if (Common::File::exists("message.map"))
+ addSource(new VolumeResourceSource("resource.msg", addExternalMap("message.map"), 0));
+
+ if (Common::File::exists("altres.map"))
+ addSource(new VolumeResourceSource("altres.000", addExternalMap("altres.map"), 0));
return 1;
}
int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
ResourceSource *map = 0;
+#ifdef ENABLE_SCI32
+ ResourceSource *sci21PatchMap = 0;
+ const Common::FSNode *sci21PatchRes = 0;
+#endif
// First, find resource.map
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -645,17 +601,33 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
Common::String filename = file->getName();
filename.toLowercase();
- // TODO: Load the SCI2.1+ maps (resmap.*) in concurrence with the volumes to
- // get the proper volume numbers from the maps.
- if (filename.contains("resource.map") || filename.contains("resmap.000")) {
+ if (filename.contains("resource.map"))
map = addExternalMap(file);
- break;
+
+ if (filename.contains("resmap.0")) {
+ const char *dot = strrchr(file->getName().c_str(), '.');
+ int number = atoi(dot + 1);
+ map = addExternalMap(file, number);
}
+
+#ifdef ENABLE_SCI32
+ // SCI2.1 resource patches
+ if (filename.contains("resmap.pat"))
+ sci21PatchMap = addExternalMap(file, 100);
+
+ if (filename.contains("ressci.pat"))
+ sci21PatchRes = file;
+#endif
}
if (!map)
return 0;
+#ifdef ENABLE_SCI32
+ if (sci21PatchMap && sci21PatchRes)
+ addSource(new VolumeResourceSource(sci21PatchRes->getName(), sci21PatchMap, 100, sci21PatchRes));
+#endif
+
// Now find all the resource.0?? files
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (file->isDirectory())
@@ -668,7 +640,7 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
const char *dot = strrchr(filename.c_str(), '.');
int number = atoi(dot + 1);
- addSource(map, kSourceVolume, file, number);
+ addSource(new VolumeResourceSource(file->getName(), map, number, file));
}
}
@@ -683,82 +655,137 @@ int ResourceManager::addInternalSources() {
Common::List<ResourceId>::iterator itr = resources->begin();
while (itr != resources->end()) {
- ResourceSource *src = addSource(NULL, kSourceIntMap, "MAP", itr->number);
+ ResourceSource *src = addSource(new IntMapResourceSource("MAP", itr->getNumber()));
- if ((itr->number == 65535) && Common::File::exists("RESOURCE.SFX"))
- addSource(src, kSourceAudioVolume, "RESOURCE.SFX", 0);
+ if ((itr->getNumber() == 65535) && Common::File::exists("RESOURCE.SFX"))
+ addSource(new AudioVolumeResourceSource(this, "RESOURCE.SFX", src, 0));
else if (Common::File::exists("RESOURCE.AUD"))
- addSource(src, kSourceAudioVolume, "RESOURCE.AUD", 0);
+ addSource(new AudioVolumeResourceSource(this, "RESOURCE.AUD", src, 0));
++itr;
}
+ delete resources;
return 1;
}
-void ResourceManager::addNewGMPatch(const Common::String &gameId) {
- Common::String gmPatchFile;
-
- if (gameId == "ecoquest")
- gmPatchFile = "ECO1GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "lsl1sci")
- gmPatchFile = "LL1_GM.PAT";
- else if (gameId == "lsl5")
- gmPatchFile = "LL5_GM.PAT";
- else if (gameId == "longbow")
- gmPatchFile = "ROBNGM.PAT";
- else if (gameId == "sq1sci")
- gmPatchFile = "SQ1_GM.PAT";
- else if (gameId == "sq4")
- gmPatchFile = "SQ4_GM.PAT";
- else if (gameId == "fairytales")
- gmPatchFile = "TALEGM.PAT";
-
- if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourcePatch;
- psrcPatch->location_name = gmPatchFile;
- processPatch(psrcPatch, kResourceTypePatch, 4);
- }
-}
-
void ResourceManager::scanNewSources() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
ResourceSource *source = *it;
- if (!source->scanned) {
- source->scanned = true;
- switch (source->source_type) {
- case kSourceDirectory:
- readResourcePatches(source);
- readWaveAudioPatches();
- break;
- case kSourceExtMap:
- if (_mapVersion < kResVersionSci1Late)
- readResourceMapSCI0(source);
- else
- readResourceMapSCI1(source);
- break;
- case kSourceExtAudioMap:
- readAudioMapSCI1(source);
- break;
- case kSourceIntMap:
- readAudioMapSCI11(source);
- break;
- case kSourceMacResourceFork:
- readMacResourceFork(source);
- break;
- default:
- break;
- }
+ if (!source->_scanned) {
+ source->_scanned = true;
+ source->scanSource(this);
}
}
}
+void DirectoryResourceSource::scanSource(ResourceManager *resMan) {
+ resMan->readResourcePatches();
+
+ // We can't use getSciVersion() at this point, thus using _volVersion
+ if (resMan->_volVersion >= kResVersionSci11) // SCI1.1+
+ resMan->readResourcePatchesBase36();
+
+ resMan->readWaveAudioPatches();
+}
+
+void ExtMapResourceSource::scanSource(ResourceManager *resMan) {
+ if (resMan->_mapVersion < kResVersionSci1Late)
+ resMan->readResourceMapSCI0(this);
+ else
+ resMan->readResourceMapSCI1(this);
+}
+
+void ExtAudioMapResourceSource::scanSource(ResourceManager *resMan) {
+ resMan->readAudioMapSCI1(this);
+}
+
+void IntMapResourceSource::scanSource(ResourceManager *resMan) {
+ resMan->readAudioMapSCI11(this);
+}
+
+#ifdef ENABLE_SCI32
+
+// Chunk resources are resources that hold other resources. They are normally called
+// when using the kLoadChunk SCI2.1 kernel function. However, for example, the Lighthouse
+// SCI2.1 demo has a chunk but no scripts outside of the chunk.
+
+// A chunk resource is pretty straightforward in terms of layout
+// It begins with 11-byte entries in the header:
+// =========
+// b resType
+// w nEntry
+// dw offset
+// dw length
+
+ChunkResourceSource::ChunkResourceSource(const Common::String &name, uint16 number)
+ : ResourceSource(kSourceChunk, name) {
+
+ _number = 0;
+}
+
+void ChunkResourceSource::scanSource(ResourceManager *resMan) {
+ Resource *chunk = resMan->findResource(ResourceId(kResourceTypeChunk, _number), false);
+
+ if (!chunk)
+ error("Trying to load non-existent chunk");
+
+ byte *ptr = chunk->data;
+ uint32 firstOffset = 0;
+
+ for (;;) {
+ ResourceType type = resMan->convertResType(*ptr);
+ uint16 number = READ_LE_UINT16(ptr + 1);
+ ResourceId id(type, number);
+
+ ResourceEntry entry;
+ entry.offset = READ_LE_UINT32(ptr + 3);
+ entry.length = READ_LE_UINT32(ptr + 7);
+
+ _resMap[id] = entry;
+ ptr += 11;
+
+ debugC(kDebugLevelResMan, 2, "Found %s in chunk %d", id.toString().c_str(), _number);
+
+ resMan->updateResource(id, this, entry.length);
+
+ // There's no end marker to the data table, but the first resource
+ // begins directly after the entry table. So, when we hit the first
+ // resource, we're at the end of the entry table.
+
+ if (!firstOffset)
+ firstOffset = entry.offset;
+
+ if ((size_t)(ptr - chunk->data) >= firstOffset)
+ break;
+ }
+}
+
+void ChunkResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Resource *chunk = resMan->findResource(ResourceId(kResourceTypeChunk, _number), false);
+
+ if (!_resMap.contains(res->_id))
+ error("Trying to load non-existent resource from chunk %d: %s %d", _number, getResourceTypeName(res->_id.getType()), res->_id.getNumber());
+
+ ResourceEntry entry = _resMap[res->_id];
+ res->data = new byte[entry.length];
+ res->size = entry.length;
+ res->_header = 0;
+ res->_headerSize = 0;
+ res->_status = kResStatusAllocated;
+
+ // Copy the resource data over
+ memcpy(res->data, chunk->data + entry.offset, entry.length);
+}
+
+void ResourceManager::addResourcesFromChunk(uint16 id) {
+ addSource(new ChunkResourceSource(Common::String::printf("Chunk %d", id), id));
+ scanNewSources();
+}
+
+#endif
+
void ResourceManager::freeResourceSources() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it)
delete *it;
@@ -767,13 +794,6 @@ void ResourceManager::freeResourceSources() {
}
ResourceManager::ResourceManager() {
- addAppropriateSources();
- init();
-}
-
-ResourceManager::ResourceManager(const Common::FSList &fslist) {
- addAppropriateSources(fslist);
- init();
}
void ResourceManager::init() {
@@ -828,8 +848,23 @@ void ResourceManager::init() {
debugC(1, kDebugLevelResMan, "resMan: Detected Amiga graphic resources");
break;
default:
- warning("resMan: Couldn't determine view type");
+ error("resMan: Couldn't determine view type");
}
+
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2_1) {
+ // If we have no scripts, but chunk 0 is present, open up the chunk
+ // to try to get to any scripts in there. The Lighthouse SCI2.1 demo
+ // does exactly this.
+
+ Common::List<ResourceId> *scriptList = listResources(kResourceTypeScript);
+
+ if (scriptList->empty() && testResource(ResourceId(kResourceTypeChunk, 0)))
+ addResourcesFromChunk(0);
+
+ delete scriptList;
+ }
+#endif
}
ResourceManager::~ResourceManager() {
@@ -907,7 +942,7 @@ Common::List<ResourceId> *ResourceManager::listResources(ResourceType type, int
ResourceMap::iterator itr = _resMap.begin();
while (itr != _resMap.end()) {
- if ((itr->_value->_id.type == type) && ((mapNumber == -1) || (itr->_value->_id.number == mapNumber)))
+ if ((itr->_value->getType() == type) && ((mapNumber == -1) || (itr->_value->getNumber() == mapNumber)))
resources->push_back(itr->_value->_id);
++itr;
}
@@ -925,7 +960,7 @@ Resource *ResourceManager::findResource(ResourceId id, bool lock) {
loadResource(retval);
else if (retval->_status == kResStatusEnqueued)
removeFromLRU(retval);
- // Unless an error occured, the resource is now either
+ // Unless an error occurred, the resource is now either
// locked or allocated, but never queued or freed.
freeOldResources();
@@ -954,7 +989,7 @@ void ResourceManager::unlockResource(Resource *res) {
assert(res);
if (res->_status != kResStatusLocked) {
- warning("[resMan] Attempt to unlock unlocked resource %s", res->_id.toString().c_str());
+ debugC(kDebugLevelResMan, 2, "[resMan] Attempt to unlock unlocked resource %s", res->_id.toString().c_str());
return;
}
@@ -988,26 +1023,25 @@ const char *ResourceManager::versionDescription(ResVersion version) const {
return "Version not valid";
}
-ResourceManager::ResVersion ResourceManager::detectMapVersion() {
+ResVersion ResourceManager::detectMapVersion() {
Common::SeekableReadStream *fileStream = 0;
- Common::File *file = 0;
byte buff[6];
ResourceSource *rsrc= 0;
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
rsrc = *it;
- if (rsrc->source_type == kSourceExtMap) {
- if (rsrc->resourceFile) {
- fileStream = rsrc->resourceFile->createReadStream();
+ if (rsrc->getSourceType() == kSourceExtMap) {
+ if (rsrc->_resourceFile) {
+ fileStream = rsrc->_resourceFile->createReadStream();
} else {
- file = new Common::File();
- file->open(rsrc->location_name);
+ Common::File *file = new Common::File();
+ file->open(rsrc->getLocationName());
if (file->isOpen())
fileStream = file;
}
break;
- } else if (rsrc->source_type == kSourceMacResourceFork)
+ } else if (rsrc->getSourceType() == kSourceMacResourceFork)
return kResVersionSci11Mac;
}
@@ -1022,7 +1056,7 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
// check if 0 or 01 - try to read resources in SCI0 format and see if exists
fileStream->seek(0, SEEK_SET);
while (fileStream->read(buff, 6) == 6 && !(buff[0] == 0xFF && buff[1] == 0xFF && buff[2] == 0xFF)) {
- if (getVolume(rsrc, (buff[5] & 0xFC) >> 2) == NULL)
+ if (findVolume(rsrc, (buff[5] & 0xFC) >> 2) == NULL)
return kResVersionSci1Middle;
}
return kResVersionSci0Sci1Early;
@@ -1079,25 +1113,24 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
return kResVersionUnknown;
}
-ResourceManager::ResVersion ResourceManager::detectVolVersion() {
+ResVersion ResourceManager::detectVolVersion() {
Common::SeekableReadStream *fileStream = 0;
- Common::File *file = 0;
ResourceSource *rsrc;
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
rsrc = *it;
- if (rsrc->source_type == kSourceVolume) {
- if (rsrc->resourceFile) {
- fileStream = rsrc->resourceFile->createReadStream();
+ if (rsrc->getSourceType() == kSourceVolume) {
+ if (rsrc->_resourceFile) {
+ fileStream = rsrc->_resourceFile->createReadStream();
} else {
- file = new Common::File();
- file->open(rsrc->location_name);
+ Common::File *file = new Common::File();
+ file->open(rsrc->getLocationName());
if (file->isOpen())
fileStream = file;
}
break;
- } else if (rsrc->source_type == kSourceMacResourceFork)
+ } else if (rsrc->getSourceType() == kSourceMacResourceFork)
return kResVersionSci11Mac;
}
@@ -1180,82 +1213,176 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() {
}
// version-agnostic patch application
-void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, int resnumber) {
- Common::File file;
- Resource *newrsc;
- ResourceId resId = ResourceId(restype, resnumber);
- byte patchtype, patch_data_offset;
- int fsize;
-
- if (resnumber == -1)
- return;
- if (!file.open(source->location_name)) {
- warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str());
- return;
+void ResourceManager::processPatch(ResourceSource *source, ResourceType resourceType, uint16 resourceNr, uint32 tuple) {
+ Common::SeekableReadStream *fileStream = 0;
+ Resource *newrsc = 0;
+ ResourceId resId = ResourceId(resourceType, resourceNr, tuple);
+ ResourceType checkForType = resourceType;
+
+ // base36 encoded patches (i.e. audio36 and sync36) have the same type as their non-base36 encoded counterparts
+ if (checkForType == kResourceTypeAudio36)
+ checkForType = kResourceTypeAudio;
+ else if (checkForType == kResourceTypeSync36)
+ checkForType = kResourceTypeSync;
+
+ if (source->_resourceFile) {
+ fileStream = source->_resourceFile->createReadStream();
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(source->getLocationName())) {
+ warning("ResourceManager::processPatch(): failed to open %s", source->getLocationName().c_str());
+ return;
+ }
+ fileStream = file;
}
- fsize = file.size();
+
+ int fsize = fileStream->size();
if (fsize < 3) {
- debug("Patching %s failed - file too small", source->location_name.c_str());
+ debug("Patching %s failed - file too small", source->getLocationName().c_str());
return;
}
- patchtype = file.readByte() & 0x7F;
- patch_data_offset = file.readByte();
+ byte patchType = convertResType(fileStream->readByte());
+ byte patchDataOffset = fileStream->readByte();
+
+ delete fileStream;
- if (patchtype != restype) {
- debug("Patching %s failed - resource type mismatch", source->location_name.c_str());
+ if (patchType != checkForType) {
+ debug("Patching %s failed - resource type mismatch", source->getLocationName().c_str());
+ return;
}
// Fixes SQ5/German, patch file special case logic taken from SCI View disassembly
- if (patch_data_offset & 0x80) {
- switch (patch_data_offset & 0x7F) {
+ if (patchDataOffset & 0x80) {
+ switch (patchDataOffset & 0x7F) {
case 0:
- patch_data_offset = 24;
+ patchDataOffset = 24;
break;
case 1:
- patch_data_offset = 2;
+ patchDataOffset = 2;
+ break;
+ case 4:
+ patchDataOffset = 8;
break;
default:
- warning("Resource patch unsupported special case %X", patch_data_offset);
+ error("Resource patch unsupported special case %X", patchDataOffset & 0x7F);
+ return;
}
}
- if (patch_data_offset + 2 >= fsize) {
+ if (patchDataOffset + 2 >= fsize) {
debug("Patching %s failed - patch starting at offset %d can't be in file of size %d",
- source->location_name.c_str(), patch_data_offset + 2, fsize);
+ source->getLocationName().c_str(), patchDataOffset + 2, fsize);
return;
}
- // Prepare destination, if neccessary
- if (_resMap.contains(resId) == false) {
- newrsc = new Resource;
- _resMap.setVal(resId, newrsc);
- } else
- newrsc = _resMap.getVal(resId);
+
// Overwrite everything, because we're patching
- newrsc->_id = resId;
- newrsc->_status = kResStatusNoMalloc;
- newrsc->_source = source;
- newrsc->size = fsize - patch_data_offset - 2;
- newrsc->_headerSize = patch_data_offset;
+ newrsc = updateResource(resId, source, fsize - patchDataOffset - 2);
+ newrsc->_headerSize = patchDataOffset;
newrsc->_fileOffset = 0;
- debugC(1, kDebugLevelResMan, "Patching %s - OK", source->location_name.c_str());
+
+
+ debugC(1, kDebugLevelResMan, "Patching %s - OK", source->getLocationName().c_str());
}
+void ResourceManager::readResourcePatchesBase36() {
+ // The base36 encoded audio36 and sync36 resources use a different naming scheme, because they
+ // cannot be described with a single resource number, but are a result of a
+ // <number, noun, verb, cond, seq> tuple. Please don't be confused with the normal audio patches
+ // (*.aud) and normal sync patches (*.syn). audio36 patches can be seen for example in the AUD
+ // folder of GK1CD, and are like this file: @0CS0M00.0X1. GK1CD is the first game where these
+ // have been observed. The actual audio36 and sync36 resources exist in SCI1.1 as well, but the
+ // first game where external patch files for them have been found is GK1CD. The names of these
+ // files are base36 encoded, and we handle their decoding here. audio36 files start with a '@',
+ // whereas sync36 start with a '#'. Mac versions begin with 'A' (probably meaning AIFF). Torin
+ // has several that begin with 'B'.
+
+ Common::String name, inputName;
+ Common::ArchiveMemberList files;
+ ResourceSource *psrcPatch;
+
+ for (int i = kResourceTypeAudio36; i <= kResourceTypeSync36; ++i) {
+ files.clear();
-void ResourceManager::readResourcePatches(ResourceSource *source) {
-// Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files
-// this function tries to read patch file with any supported naming scheme,
-// regardless of s_sciVersion value
+ // audio36 resources start with a @, A, or B
+ // sync36 resources start with a #
+ if (i == kResourceTypeAudio36) {
+ SearchMan.listMatchingMembers(files, "@???????.???");
+ SearchMan.listMatchingMembers(files, "A???????.???");
+ SearchMan.listMatchingMembers(files, "B???????.???");
+ } else
+ SearchMan.listMatchingMembers(files, "#???????.???");
+
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ name = (*x)->getName();
+
+ inputName = (*x)->getName();
+ inputName.toUppercase();
+ inputName.deleteChar(0); // delete the first character (type)
+ inputName.deleteChar(7); // delete the dot
+
+ // The base36 encoded resource contains the following:
+ // uint16 resourceId, byte noun, byte verb, byte cond, byte seq
+ uint16 resourceNr = strtol(Common::String(inputName.c_str(), 3).c_str(), 0, 36); // 3 characters
+ uint16 noun = strtol(Common::String(inputName.c_str() + 3, 2).c_str(), 0, 36); // 2 characters
+ uint16 verb = strtol(Common::String(inputName.c_str() + 5, 2).c_str(), 0, 36); // 2 characters
+ uint16 cond = strtol(Common::String(inputName.c_str() + 7, 2).c_str(), 0, 36); // 2 characters
+ uint16 seq = strtol(Common::String(inputName.c_str() + 9, 1).c_str(), 0, 36); // 1 character
+
+ // Check, if we got valid results
+ if ((noun <= 255) && (verb <= 255) && (cond <= 255) && (seq <= 255)) {
+ ResourceId resource36((ResourceType)i, resourceNr, noun, verb, cond, seq);
+
+ /*
+ if (i == kResourceTypeAudio36)
+ debug("audio36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), resource36.tuple, resource36.toString().c_str());
+ else
+ debug("sync36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), resource36.tuple, resource36.toString().c_str());
+ */
+
+ // Make sure that the audio patch is a valid resource
+ if (i == kResourceTypeAudio36) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(name);
+ uint32 tag = stream->readUint32BE();
+
+ if (tag == MKID_BE('RIFF') || tag == MKID_BE('FORM')) {
+ delete stream;
+ processWavePatch(resource36, name);
+ continue;
+ }
+
+ // Check for SOL as well
+ tag = (tag << 16) | stream->readUint16BE();
+
+ if (tag != MKID_BE('SOL\0')) {
+ delete stream;
+ continue;
+ }
+
+ delete stream;
+ }
+
+ psrcPatch = new PatchResourceSource(name);
+ processPatch(psrcPatch, (ResourceType)i, resourceNr, resource36.getTuple());
+ }
+ }
+ }
+}
+
+void ResourceManager::readResourcePatches() {
+ // Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files
+ // this function tries to read patch file with any supported naming scheme,
+ // regardless of s_sciVersion value
Common::String mask, name;
Common::ArchiveMemberList files;
- int number = -1;
+ uint16 resourceNr = 0;
const char *szResType;
ResourceSource *psrcPatch;
- for (int i = kResourceTypeView; i <= kResourceTypeRobot; ++i) {
- // TODO: add support for audio36 and sync36 files
- if (i == kResourceTypeAudio36 || i == kResourceTypeSync36)
+ for (int i = kResourceTypeView; i < kResourceTypeInvalid; ++i) {
+ // Ignore the types that can't be patched (and Robot/VMD is handled externally for now)
+ if (!s_resourceTypeSuffixes[i] || i == kResourceTypeRobot || i == kResourceTypeVMD)
continue;
files.clear();
@@ -1266,112 +1393,77 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
SearchMan.listMatchingMembers(files, mask);
// SCI1 and later naming - nnn.typ
mask = "*.";
- mask += resourceTypeSuffixes[i];
+ mask += s_resourceTypeSuffixes[i];
SearchMan.listMatchingMembers(files, mask);
+
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
bool bAdd = false;
name = (*x)->getName();
+
// SCI1 scheme
if (isdigit(name[0])) {
- number = atoi(name.c_str());
- bAdd = true;
+ char *end = 0;
+ resourceNr = strtol(name.c_str(), &end, 10);
+ bAdd = (*end == '.'); // Ensure the next character is the period
} else {
// SCI0 scheme
int resname_len = strlen(szResType);
if (scumm_strnicmp(name.c_str(), szResType, resname_len) == 0
&& !isalpha(name[resname_len + 1])) {
- number = atoi(name.c_str() + resname_len + 1);
+ resourceNr = atoi(name.c_str() + resname_len + 1);
bAdd = true;
}
}
if (bAdd) {
- psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourcePatch;
- psrcPatch->location_name = name;
- processPatch(psrcPatch, (ResourceType)i, number);
+ psrcPatch = new PatchResourceSource(name);
+ processPatch(psrcPatch, (ResourceType)i, resourceNr);
}
}
}
}
-void ResourceManager::readWaveAudioPatches() {
- // Here we do check for SCI1.1+ so we can patch wav files in as audio resources
- Common::ArchiveMemberList files;
- SearchMan.listMatchingMembers(files, "*.wav");
-
- for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
- Common::String name = (*x)->getName();
-
- if (isdigit(name[0])) {
- int number = atoi(name.c_str());
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourceWave;
- psrcPatch->location_name = name;
- psrcPatch->volume_number = 0;
- psrcPatch->audioCompressionType = 0;
-
- ResourceId resId = ResourceId(kResourceTypeAudio, number);
-
- Resource *newrsc = NULL;
-
- // Prepare destination, if neccessary
- if (_resMap.contains(resId) == false) {
- newrsc = new Resource;
- _resMap.setVal(resId, newrsc);
- } else
- newrsc = _resMap.getVal(resId);
-
- // Get the size of the file
- Common::SeekableReadStream *stream = (*x)->createReadStream();
- uint32 fileSize = stream->size();
- delete stream;
-
- // Overwrite everything, because we're patching
- newrsc->_id = resId;
- newrsc->_status = kResStatusNoMalloc;
- newrsc->_source = psrcPatch;
- newrsc->size = fileSize;
- newrsc->_headerSize = 0;
- debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str());
- }
- }
-}
-
int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
- Common::File file;
- Resource *res;
+ Common::SeekableReadStream *fileStream = 0;
ResourceType type;
uint16 number, id;
uint32 offset;
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
+ if (map->_resourceFile) {
+ fileStream = map->_resourceFile->createReadStream();
+ if (!fileStream)
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(map->getLocationName()))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ fileStream = file;
+ }
- file.seek(0, SEEK_SET);
+ fileStream->seek(0, SEEK_SET);
byte bMask = (_mapVersion == kResVersionSci1Middle) ? 0xF0 : 0xFC;
byte bShift = (_mapVersion == kResVersionSci1Middle) ? 28 : 26;
do {
- id = file.readUint16LE();
- offset = file.readUint32LE();
+ id = fileStream->readUint16LE();
+ offset = fileStream->readUint32LE();
- if (file.eos() || file.err()) {
- warning("Error while reading %s", map->location_name.c_str());
+ if (fileStream->eos() || fileStream->err()) {
+ delete fileStream;
+ warning("Error while reading %s", map->getLocationName().c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
if (offset == 0xFFFFFFFF)
break;
- type = (ResourceType)(id >> 11);
+ type = convertResType(id >> 11);
number = id & 0x7FF;
ResourceId resId = ResourceId(type, number);
// adding a new resource
if (_resMap.contains(resId) == false) {
- res = new Resource;
- res->_source = getVolume(map, offset >> bShift);
- if (!res->_source) {
+ ResourceSource *source = findVolume(map, offset >> bShift);
+ if (!source) {
warning("Could not get volume for resource %d, VolumeID %d", id, offset >> bShift);
if (_mapVersion != _volVersion) {
warning("Retrying with the detected volume version instead");
@@ -1379,22 +1471,31 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
_mapVersion = _volVersion;
bMask = (_mapVersion == kResVersionSci1Middle) ? 0xF0 : 0xFC;
bShift = (_mapVersion == kResVersionSci1Middle) ? 28 : 26;
- res->_source = getVolume(map, offset >> bShift);
+ source = findVolume(map, offset >> bShift);
}
}
- res->_fileOffset = offset & (((~bMask) << 24) | 0xFFFFFF);
- res->_id = resId;
- _resMap.setVal(resId, res);
+
+ addResource(resId, source, offset & (((~bMask) << 24) | 0xFFFFFF));
}
- } while (!file.eos());
+ } while (!fileStream->eos());
+
+ delete fileStream;
return 0;
}
int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
- Common::File file;
- Resource *res;
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
+ Common::SeekableReadStream *fileStream = 0;
+
+ if (map->_resourceFile) {
+ fileStream = map->_resourceFile->createReadStream();
+ if (!fileStream)
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(map->getLocationName()))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ fileStream = file;
+ }
resource_index_t resMap[32];
memset(resMap, 0, sizeof(resource_index_t) * 32);
@@ -1405,8 +1506,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// Read resource type and offsets to resource offsets block from .MAP file
// The last entry has type=0xFF (0x1F) and offset equals to map file length
do {
- type = file.readByte() & 0x1F;
- resMap[type].wOffset = file.readUint16LE();
+ type = fileStream->readByte() & 0x1F;
+ resMap[type].wOffset = fileStream->readUint16LE();
resMap[prevtype].wSize = (resMap[type].wOffset
- resMap[prevtype].wOffset) / nEntrySize;
prevtype = type;
@@ -1417,18 +1518,18 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
for (type = 0; type < 32; type++) {
if (resMap[type].wOffset == 0) // this resource does not exist in map
continue;
- file.seek(resMap[type].wOffset);
+ fileStream->seek(resMap[type].wOffset);
for (int i = 0; i < resMap[type].wSize; i++) {
- uint16 number = file.readUint16LE();
+ uint16 number = fileStream->readUint16LE();
int volume_nr = 0;
if (_mapVersion == kResVersionSci11) {
// offset stored in 3 bytes
- off = file.readUint16LE();
- off |= file.readByte() << 16;
+ off = fileStream->readUint16LE();
+ off |= fileStream->readByte() << 16;
off <<= 1;
} else {
// offset/volume stored in 4 bytes
- off = file.readUint32LE();
+ off = fileStream->readUint32LE();
if (_mapVersion < kResVersionSci11) {
volume_nr = off >> 28; // most significant 4 bits
off &= 0x0FFFFFFF; // least significant 28 bits
@@ -1436,26 +1537,30 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// in SCI32 it's a plain offset
}
}
- if (file.eos() || file.err()) {
- warning("Error while reading %s", map->location_name.c_str());
+ if (fileStream->eos() || fileStream->err()) {
+ delete fileStream;
+ warning("Error while reading %s", map->getLocationName().c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
- resId = ResourceId((ResourceType)type, number);
+ resId = ResourceId(convertResType(type), number);
// adding new resource only if it does not exist
if (_resMap.contains(resId) == false) {
- res = new Resource;
- _resMap.setVal(resId, res);
- res->_id = resId;
-
// NOTE: We add the map's volume number here to the specified volume number
- // for SCI2.1 and SCI3 maps that are not RESMAP.000. The RESMAP.* files' numbers
+ // for SCI2.1 and SCI3 maps that are not resmap.000. The resmap.* files' numbers
// need to be used in concurrence with the volume specified in the map to get
// the actual resource file.
- res->_source = getVolume(map, volume_nr + map->volume_number);
- res->_fileOffset = off;
+ int mapVolumeNr = volume_nr + map->_volumeNumber;
+ ResourceSource *source = findVolume(map, mapVolumeNr);
+ // FIXME: this code has serious issues with multiple RESMAP.* files (like in unmodified gk2)
+ // adding a resource with source == NULL would crash later on
+ if (!source)
+ error("Unable to find volume for map %s volumeNr %d", map->getLocationName().c_str(), mapVolumeNr);
+ addResource(resId, source, off);
}
}
}
+
+ delete fileStream;
return 0;
}
@@ -1476,7 +1581,11 @@ struct {
{ MKID_BE('PAL '), kResourceTypePalette },
{ MKID_BE('snd '), kResourceTypeAudio },
{ MKID_BE('MSG '), kResourceTypeMessage },
- { MKID_BE('HEP '), kResourceTypeHeap }
+ { MKID_BE('HEP '), kResourceTypeHeap },
+ { MKID_BE('IBIN'), kResourceTypeMacIconBarPictN },
+ { MKID_BE('IBIS'), kResourceTypeMacIconBarPictS },
+ { MKID_BE('PICT'), kResourceTypeMacPict },
+ { MKID_BE('SYN '), kResourceTypeSync }
};
static uint32 resTypeToMacTag(ResourceType type) {
@@ -1487,11 +1596,11 @@ static uint32 resTypeToMacTag(ResourceType type) {
return 0;
}
-int ResourceManager::readMacResourceFork(ResourceSource *source) {
- if (!source->macResMan.open(source->location_name.c_str()))
- error("%s is not a valid Mac resource fork", source->location_name.c_str());
+void MacResourceForkResourceSource::scanSource(ResourceManager *resMan) {
+ if (!_macResMan->open(getLocationName().c_str()))
+ error("%s is not a valid Mac resource fork", getLocationName().c_str());
- Common::MacResTagArray tagArray = source->macResMan.getResTagArray();
+ Common::MacResTagArray tagArray = _macResMan->getResTagArray();
for (uint32 i = 0; i < tagArray.size(); i++) {
ResourceType type = kResourceTypeInvalid;
@@ -1506,306 +1615,59 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) {
if (type == kResourceTypeInvalid)
continue;
- Common::MacResIDArray idArray = source->macResMan.getResIDArray(tagArray[i]);
+ Common::MacResIDArray idArray = _macResMan->getResIDArray(tagArray[i]);
for (uint32 j = 0; j < idArray.size(); j++) {
- ResourceId resId = ResourceId(type, idArray[j]);
-
- Resource *newrsc = NULL;
+ // Get the size of the file
+ Common::SeekableReadStream *stream = _macResMan->getResource(tagArray[i], idArray[j]);
- // Prepare destination, if neccessary. Resource forks may contain patches.
- if (!_resMap.contains(resId)) {
- newrsc = new Resource;
- _resMap.setVal(resId, newrsc);
- } else
- newrsc = _resMap.getVal(resId);
+ // Some IBIS resources have a size of 0, so we skip them
+ if (!stream)
+ continue;
- // Get the size of the file
- Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]);
uint32 fileSize = stream->size();
delete stream;
- // Overwrite everything
- newrsc->_id = resId;
- newrsc->_status = kResStatusNoMalloc;
- newrsc->_source = source;
- newrsc->size = fileSize;
- newrsc->_headerSize = 0;
+ ResourceId resId = ResourceId(type, idArray[j]);
+
+ // Overwrite Resource instance. Resource forks may contain patches.
+ resMan->updateResource(resId, this, fileSize);
}
}
-
- return 0;
}
void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32 offset, uint32 size) {
// Adding new resource only if it does not exist
if (_resMap.contains(resId) == false) {
- Resource *res = new Resource;
+ Resource *res = new Resource(this, resId);
_resMap.setVal(resId, res);
- res->_id = resId;
res->_source = src;
res->_fileOffset = offset;
res->size = size;
}
}
-void ResourceManager::removeAudioResource(ResourceId resId) {
- // Remove resource, unless it was loaded from a patch
- if (_resMap.contains(resId)) {
- Resource *res = _resMap.getVal(resId);
+Resource *ResourceManager::updateResource(ResourceId resId, ResourceSource *src, uint32 size) {
+ // Update a patched resource, whether it exists or not
+ Resource *res = 0;
- if (res->_source->source_type == kSourceAudioVolume) {
- if (res->_status == kResStatusLocked) {
- warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
- } else {
- if (res->_status == kResStatusEnqueued)
- removeFromLRU(res);
-
- _resMap.erase(resId);
- delete res;
- }
- }
- }
-}
-
-// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD):
-// =========
-// 6-byte entries:
-// w nEntry
-// dw offset
-
-// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX):
-// =========
-// 5-byte entries:
-// w nEntry
-// tb offset (cumulative)
-
-// Early SCI1.1 MAP structure:
-// ===============
-// 10-byte entries:
-// b noun
-// b verb
-// b cond
-// b seq
-// dw offset
-// w syncSize + syncAscSize
-
-// Late SCI1.1 MAP structure:
-// ===============
-// Header:
-// dw baseOffset
-// Followed by 7 or 11-byte entries:
-// b noun
-// b verb
-// b cond
-// b seq
-// tb cOffset (cumulative offset)
-// w syncSize (iff seq has bit 7 set)
-// w syncAscSize (iff seq has bit 6 set)
-
-int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
- bool isEarly = true;
- uint32 offset = 0;
- Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false);
-
- if (!mapRes) {
- warning("Failed to open %i.MAP", map->volume_number);
- return SCI_ERROR_RESMAP_NOT_FOUND;
- }
-
- ResourceSource *src = getVolume(map, 0);
-
- if (!src)
- return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
-
- byte *ptr = mapRes->data;
-
- if (map->volume_number == 65535) {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff))
- isEarly = false;
-
- while (ptr < mapRes->data + mapRes->size) {
- uint16 n = READ_LE_UINT16(ptr);
- ptr += 2;
-
- if (n == 0xffff)
- break;
-
- if (isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- } else {
- offset += READ_LE_UINT24(ptr);
- ptr += 3;
- }
-
- addResource(ResourceId(kResourceTypeAudio, n), src, offset);
- }
+ if (_resMap.contains(resId)) {
+ res = _resMap.getVal(resId);
} else {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff))
- isEarly = false;
-
- if (!isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- }
-
- while (ptr < mapRes->data + mapRes->size) {
- uint32 n = READ_BE_UINT32(ptr);
- int syncSize = 0;
- ptr += 4;
-
- if (n == 0xffffffff)
- break;
-
- if (isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- } else {
- offset += READ_LE_UINT24(ptr);
- ptr += 3;
- }
-
- if (isEarly || (n & 0x80)) {
- syncSize = READ_LE_UINT16(ptr);
- ptr += 2;
-
- if (syncSize > 0)
- addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize);
- }
-
- if (n & 0x40) {
- syncSize += READ_LE_UINT16(ptr);
- ptr += 2;
- }
-
- addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize);
- }
- }
-
- return 0;
-}
-
-// AUDIOnnn.MAP contains 10-byte entries:
-// Early format:
-// w 5 bits resource type and 11 bits resource number
-// dw 7 bits volume number and 25 bits offset
-// dw size
-// Later format:
-// w nEntry
-// dw offset+volume (as in resource.map)
-// dw size
-// ending with 10 0xFFs
-int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
- Common::File file;
-
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
-
- bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
- file.seek(0);
-
- while (1) {
- uint16 n = file.readUint16LE();
- uint32 offset = file.readUint32LE();
- uint32 size = file.readUint32LE();
-
- if (file.eos() || file.err()) {
- warning("Error while reading %s", map->location_name.c_str());
- return SCI_ERROR_RESMAP_NOT_FOUND;
- }
-
- if (n == 0xffff)
- break;
-
- byte volume_nr;
-
- if (oldFormat) {
- n &= 0x07ff; // Mask out resource type
- volume_nr = offset >> 25; // most significant 7 bits
- offset &= 0x01ffffff; // least significant 25 bits
- } else {
- volume_nr = offset >> 28; // most significant 4 bits
- offset &= 0x0fffffff; // least significant 28 bits
- }
-
- ResourceSource *src = getVolume(map, volume_nr);
-
- if (src) {
- if (unload)
- removeAudioResource(ResourceId(kResourceTypeAudio, n));
- else
- addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
- } else {
- warning("Failed to find audio volume %i", volume_nr);
- }
- }
-
- return 0;
-}
-
-void ResourceManager::setAudioLanguage(int language) {
- if (_audioMapSCI1) {
- if (_audioMapSCI1->volume_number == language) {
- // This language is already loaded
- return;
- }
-
- // We already have a map loaded, so we unload it first
- readAudioMapSCI1(_audioMapSCI1, true);
-
- // Remove all volumes that use this map from the source list
- Common::List<ResourceSource *>::iterator it = _sources.begin();
- while (it != _sources.end()) {
- ResourceSource *src = *it;
- if (src->associated_map == _audioMapSCI1) {
- it = _sources.erase(it);
- delete src;
- } else {
- ++it;
- }
- }
-
- // Remove the map itself from the source list
- _sources.remove(_audioMapSCI1);
- delete _audioMapSCI1;
-
- _audioMapSCI1 = NULL;
- }
-
- char filename[9];
- snprintf(filename, 9, "AUDIO%03d", language);
-
- Common::String fullname = Common::String(filename) + ".MAP";
- if (!Common::File::exists(fullname)) {
- warning("No audio map found for language %i", language);
- return;
- }
-
- _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language);
-
- // Search for audio volumes for this language and add them to the source list
- Common::ArchiveMemberList files;
- SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??");
- for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
- const Common::String name = (*x)->getName();
- const char *dot = strrchr(name.c_str(), '.');
- int number = atoi(dot + 1);
-
- addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number);
+ res = new Resource(this, resId);
+ _resMap.setVal(resId, res);
}
- scanNewSources();
-}
+ res->_status = kResStatusNoMalloc;
+ res->_source = src;
+ res->_headerSize = 0;
+ res->size = size;
-int ResourceManager::getAudioLanguage() const {
- return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0);
+ return res;
}
-int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream *file,
- uint32&szPacked, ResourceCompression &compression) {
+int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream *file,
+ uint32 &szPacked, ResourceCompression &compression) {
// SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes
// SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes
// SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes
@@ -1814,25 +1676,25 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
uint32 wCompression, szUnpacked;
ResourceType type;
- switch (_volVersion) {
+ switch (volVersion) {
case kResVersionSci0Sci1Early:
case kResVersionSci1Middle:
w = file->readUint16LE();
- type = (ResourceType)(w >> 11);
+ type = _resMan->convertResType(w >> 11);
number = w & 0x7FF;
szPacked = file->readUint16LE() - 4;
szUnpacked = file->readUint16LE();
wCompression = file->readUint16LE();
break;
case kResVersionSci1Late:
- type = (ResourceType)(file->readByte() & 0x7F);
+ type = _resMan->convertResType(file->readByte());
number = file->readUint16LE();
szPacked = file->readUint16LE() - 4;
szUnpacked = file->readUint16LE();
wCompression = file->readUint16LE();
break;
case kResVersionSci11:
- type = (ResourceType)(file->readByte() & 0x7F);
+ type = _resMan->convertResType(file->readByte());
number = file->readUint16LE();
szPacked = file->readUint16LE();
szUnpacked = file->readUint16LE();
@@ -1841,15 +1703,15 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
case kResVersionSci11Mac:
// Doesn't store this data in the resource. Fortunately,
// we already have this data.
- type = res->_id.type;
- number = res->_id.number;
+ type = getType();
+ number = getNumber();
szPacked = file->size();
szUnpacked = file->size();
wCompression = 0;
break;
#ifdef ENABLE_SCI32
case kResVersionSci32:
- type = (ResourceType)(file->readByte() & 0x7F);
+ type = _resMan->convertResType(file->readByte());
number = file->readUint16LE();
szPacked = file->readUint32LE();
szUnpacked = file->readUint32LE();
@@ -1864,8 +1726,8 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
if ((file->eos() || file->err()))
return SCI_ERROR_IO_ERROR;
- res->_id = ResourceId(type, number);
- res->size = szUnpacked;
+ _id = ResourceId(type, number);
+ size = szUnpacked;
// checking compression method
switch (wCompression) {
@@ -1901,15 +1763,15 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
return compression == kCompUnknown ? SCI_ERROR_UNKNOWN_COMPRESSION : 0;
}
-int ResourceManager::decompress(Resource *res, Common::SeekableReadStream *file) {
- int error;
+int Resource::decompress(ResVersion volVersion, Common::SeekableReadStream *file) {
+ int errorNum;
uint32 szPacked = 0;
ResourceCompression compression = kCompUnknown;
// fill resource info
- error = readResourceInfo(res, file, szPacked, compression);
- if (error)
- return error;
+ errorNum = readResourceInfo(volVersion, file, szPacked, compression);
+ if (errorNum)
+ return errorNum;
// getting a decompressor
Decompressor *dec = NULL;
@@ -1935,18 +1797,18 @@ int ResourceManager::decompress(Resource *res, Common::SeekableReadStream *file)
break;
#endif
default:
- warning("Resource %s: Compression method %d not supported", res->_id.toString().c_str(), compression);
+ error("Resource %s: Compression method %d not supported", _id.toString().c_str(), compression);
return SCI_ERROR_UNKNOWN_COMPRESSION;
}
- res->data = new byte[res->size];
- res->_status = kResStatusAllocated;
- error = res->data ? dec->unpack(file, res->data, szPacked, res->size) : SCI_ERROR_RESOURCE_TOO_BIG;
- if (error)
- res->unalloc();
+ data = new byte[size];
+ _status = kResStatusAllocated;
+ errorNum = data ? dec->unpack(file, data, szPacked, size) : SCI_ERROR_RESOURCE_TOO_BIG;
+ if (errorNum)
+ unalloc();
delete dec;
- return error;
+ return errorNum;
}
ResourceCompression ResourceManager::getViewCompression() {
@@ -1954,25 +1816,32 @@ ResourceCompression ResourceManager::getViewCompression() {
// Test 10 views to see if any are compressed
for (int i = 0; i < 1000; i++) {
- Common::File *file;
+ Common::SeekableReadStream *fileStream = 0;
Resource *res = testResource(ResourceId(kResourceTypeView, i));
if (!res)
continue;
- if (res->_source->source_type != kSourceVolume)
+ if (res->_source->getSourceType() != kSourceVolume)
continue;
- file = getVolumeFile(res->_source->location_name.c_str());
- if (!file)
+ fileStream = getVolumeFile(res->_source);
+
+ if (!fileStream)
continue;
- file->seek(res->_fileOffset, SEEK_SET);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
uint32 szPacked;
ResourceCompression compression;
- if (readResourceInfo(res, file, szPacked, compression))
+ if (res->readResourceInfo(_volVersion, fileStream, szPacked, compression)) {
+ if (res->_source->_resourceFile)
+ delete fileStream;
continue;
+ }
+
+ if (res->_source->_resourceFile)
+ delete fileStream;
if (compression != kCompNone)
return compression;
@@ -1989,6 +1858,10 @@ ViewType ResourceManager::detectViewType() {
Resource *res = findResource(ResourceId(kResourceTypeView, i), 0);
if (res) {
+ // Skip views coming from patch files
+ if (res->_source->getSourceType() == kSourcePatch)
+ continue;
+
switch (res->data[1]) {
case 128:
// If the 2nd byte is 128, it's a VGA game
@@ -2044,6 +1917,7 @@ ViewType ResourceManager::detectViewType() {
}
}
+ // this may happen if there are serious system issues (or trying to add a broken game)
warning("resMan: Couldn't find any views");
return kViewUnknown;
}
@@ -2129,18 +2003,18 @@ void ResourceManager::detectSciVersion() {
return;
}
+ if (hasSci0Voc999()) {
+ s_sciVersion = SCI_VERSION_0_LATE;
+ return;
+ }
+
if (oldDecompressors) {
// It's either SCI_VERSION_0_LATE or SCI_VERSION_01
// We first check for SCI1 vocab.999
if (testResource(ResourceId(kResourceTypeVocab, 999))) {
- if (hasSci0Voc999()) {
- s_sciVersion = SCI_VERSION_0_LATE;
- return;
- } else {
- s_sciVersion = SCI_VERSION_01;
- return;
- }
+ s_sciVersion = SCI_VERSION_01;
+ return;
}
// If vocab.999 is missing, we try vocab.900
@@ -2154,7 +2028,7 @@ void ResourceManager::detectSciVersion() {
}
}
- warning("Failed to accurately determine SCI version");
+ error("Failed to accurately determine SCI version");
// No parser, we assume SCI_VERSION_01.
s_sciVersion = SCI_VERSION_01;
return;
@@ -2166,8 +2040,7 @@ void ResourceManager::detectSciVersion() {
return;
}
- // SCI_VERSION_1_EARLY EGA versions seem to be lacking a valid vocab.900.
- // If this turns out to be unreliable, we could do some pic resource checks instead.
+ // SCI_VERSION_1_EARLY EGA versions lack the parser vocab
s_sciVersion = SCI_VERSION_1_EARLY;
return;
case kResVersionSci1Middle:
@@ -2203,15 +2076,18 @@ bool ResourceManager::detectHires() {
// SCI32 picture
uint16 width = READ_LE_UINT16(res->data + 10);
uint16 height = READ_LE_UINT16(res->data + 12);
+ // Surely lowres (e.g. QFG4CD)
if ((width == 320) && ((height == 190) || (height == 200)))
return false;
+ // Surely hires
if ((width >= 600) || (height >= 400))
return true;
}
}
}
- warning("resMan: Couldn't detect hires");
+ // We haven't been able to find hires content
+
return false;
#else
error("no sci32 support");
@@ -2231,12 +2107,27 @@ bool ResourceManager::detectFontExtended() {
return false;
}
+// detects, if SCI1.1 game uses palette merging or copying - this is supposed to only get used on SCI1.1 games
+bool ResourceManager::detectForPaletteMergingForSci11() {
+ // Load palette 999 (default palette)
+ Resource *res = findResource(ResourceId(kResourceTypePalette, 999), false);
+
+ if ((res) && (res->size > 30)) {
+ byte *data = res->data;
+ // Old palette format used in palette resource? -> it's merging
+ if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0))
+ return true;
+ return false;
+ }
+ return false;
+}
+
// Functions below are based on PD code by Brian Provinciano (SCI Studio)
bool ResourceManager::hasOldScriptHeader() {
Resource *res = findResource(ResourceId(kResourceTypeScript, 0), 0);
if (!res) {
- warning("resMan: Failed to find script.000");
+ error("resMan: Failed to find script.000");
return false;
}
@@ -2329,248 +2220,98 @@ bool ResourceManager::hasSci1Voc900() {
return offset == res->size;
}
-SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
- Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true);
- int trackNr, channelNr;
- if (!resource)
- return;
-
- _innerResource = resource;
-
- byte *data, *data2;
- byte *dataEnd;
- Channel *channel, *sampleChannel;
-
- switch (_soundVersion) {
- case SCI_VERSION_0_EARLY:
- case SCI_VERSION_0_LATE:
- // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards
- _trackCount = 1;
- _tracks = new Track[_trackCount];
- _tracks->digitalChannelNr = -1;
- _tracks->type = 0; // Not used for SCI0
- _tracks->channelCount = 1;
- // Digital sample data included? -> Add an additional channel
- if (resource->data[0] == 2)
- _tracks->channelCount++;
- _tracks->channels = new Channel[_tracks->channelCount];
- memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount);
- channel = &_tracks->channels[0];
- if (_soundVersion == SCI_VERSION_0_EARLY) {
- channel->data = resource->data + 0x11;
- channel->size = resource->size - 0x11;
- } else {
- channel->data = resource->data + 0x21;
- channel->size = resource->size - 0x21;
- }
- if (_tracks->channelCount == 2) {
- // Digital sample data included
- _tracks->digitalChannelNr = 1;
- sampleChannel = &_tracks->channels[1];
- // we need to find 0xFC (channel terminator) within the data
- data = channel->data;
- dataEnd = channel->data + channel->size;
- while ((data < dataEnd) && (*data != 0xfc))
- data++;
- // Skip any following 0xFCs as well
- while ((data < dataEnd) && (*data == 0xfc))
- data++;
- // Now adjust channels accordingly
- sampleChannel->data = data;
- sampleChannel->size = channel->size - (data - channel->data);
- channel->size = data - channel->data;
- // Read sample header information
- //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer.
- _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14);
- _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32);
- _tracks->digitalSampleStart = 0;
- _tracks->digitalSampleEnd = 0;
- sampleChannel->data += 44; // Skip over header
- sampleChannel->size -= 44;
- }
- break;
-
- case SCI_VERSION_1_EARLY:
- case SCI_VERSION_1_LATE:
- data = resource->data;
- // Count # of tracks
- _trackCount = 0;
- while ((*data++) != 0xFF) {
- _trackCount++;
- while (*data != 0xFF)
- data += 6;
- data++;
- }
- _tracks = new Track[_trackCount];
- data = resource->data;
- for (trackNr = 0; trackNr < _trackCount; trackNr++) {
- // Track info starts with track type:BYTE
- // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD
- // 0xFF:BYTE as terminator to end that track and begin with another track type
- // Track type 0xFF is the marker signifying the end of the tracks
-
- _tracks[trackNr].type = *data++;
- // Counting # of channels used
- data2 = data;
- _tracks[trackNr].channelCount = 0;
- while (*data2 != 0xFF) {
- data2 += 6;
- _tracks[trackNr].channelCount++;
- }
- _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount];
- _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated
- _tracks[trackNr].digitalSampleRate = 0;
- _tracks[trackNr].digitalSampleSize = 0;
- _tracks[trackNr].digitalSampleStart = 0;
- _tracks[trackNr].digitalSampleEnd = 0;
- if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently
- for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) {
- channel = &_tracks[trackNr].channels[channelNr];
- channel->prio = READ_LE_UINT16(data);
- channel->data = resource->data + READ_LE_UINT16(data + 2) + 2;
- channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header
- channel->number = *(channel->data - 2);
- channel->poly = *(channel->data - 1);
- channel->time = channel->prev = 0;
- if (channel->number == 0xFE) { // Digital channel
- _tracks[trackNr].digitalChannelNr = channelNr;
- _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data);
- _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2);
- _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4);
- _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6);
- channel->data += 8; // Skip over header
- channel->size -= 8;
- }
- data += 6;
- }
- } else {
- // Skip over digital track
- data += 6;
- }
- data++; // Skipping 0xFF that closes channels list
- }
- break;
-
- default:
- error("SoundResource: SCI version %d is unsupported", _soundVersion);
- }
-}
-
-SoundResource::~SoundResource() {
- for (int trackNr = 0; trackNr < _trackCount; trackNr++)
- delete[] _tracks[trackNr].channels;
- delete[] _tracks;
+// Same function as Script::findBlock(). Slight code
+// duplication here, but this has been done to keep the resource
+// manager independent from the rest of the engine
+static byte *findSci0ExportsBlock(byte *buffer) {
+ byte *buf = buffer;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- _resMan->unlockResource(_innerResource);
-}
+ if (oldScriptHeader)
+ buf += 2;
-#if 0
-SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) {
- if (_soundVersion <= SCI_VERSION_0_LATE)
- return &_tracks[0];
+ do {
+ int seekerType = READ_LE_UINT16(buf);
- if (/*number >= 0 &&*/number < _trackCount)
- return &_tracks[number];
- return NULL;
-}
-#endif
+ if (seekerType == 0)
+ break;
+ if (seekerType == 7) // exports
+ return buf;
-SoundResource::Track *SoundResource::getTrackByType(byte type) {
- if (_soundVersion <= SCI_VERSION_0_LATE)
- return &_tracks[0];
+ int seekerSize = READ_LE_UINT16(buf + 2);
+ assert(seekerSize > 0);
+ buf += seekerSize;
+ } while (1);
- for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
- if (_tracks[trackNr].type == type)
- return &_tracks[trackNr];
- }
return NULL;
}
-SoundResource::Track *SoundResource::getDigitalTrack() {
- for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
- if (_tracks[trackNr].digitalChannelNr != -1)
- return &_tracks[trackNr];
- }
- return NULL;
-}
-
-// Gets the filter mask for SCI0 sound resources
-int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
- byte *data = _innerResource->data;
- int channelMask = 0;
-
- if (_soundVersion > SCI_VERSION_0_LATE)
- return 0;
+reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
+ Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false);
- data++; // Skip over digital sample flag
+ if (!script)
+ return NULL_REG;
- for (int channelNr = 0; channelNr < 16; channelNr++) {
- channelMask = channelMask >> 1;
+ byte *offsetPtr = 0;
- byte flags;
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data;
- if (_soundVersion == SCI_VERSION_0_EARLY) {
- // Each channel is specified by a single byte
- // Upper 4 bits of the byte is a voices count
- // Lower 4 bits -> bit 0 set: use for AdLib
- // bit 1 set: use for PCjr
- // bit 2 set: use for PC speaker
- // bit 3 set and bit 0 clear: control channel (15)
- // bit 3 set and bit 0 set: rhythm channel (9)
- // Note: control channel is dynamically assigned inside the drivers,
- // but seems to be fixed at 15 in the song data.
- flags = *data++;
-
- // Get device bits
- flags &= 0x7;
+ // Check if the first block is the exports block (in most cases, it is)
+ bool exportsIsFirst = (READ_LE_UINT16(buf + 4) == 7);
+ if (exportsIsFirst) {
+ offsetPtr = buf + 4 + 2;
} else {
- // Each channel is specified by 2 bytes
- // 1st byte is voices count
- // 2nd byte is play mask, which specifies if the channel is supposed to be played
- // by the corresponding hardware
-
- // Skip voice count
- data++;
-
- flags = *data++;
+ offsetPtr = findSci0ExportsBlock(script->data);
+ if (!offsetPtr)
+ error("Unable to find exports block from script 0");
+ offsetPtr += 4 + 2;
}
+ } else {
+ offsetPtr = script->data + 4 + 2 + 2;
+ }
+
+ int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
- bool play;
- switch (channelNr) {
- case 15:
- // Always play control channel
- play = true;
- break;
- case 9:
- // Play rhythm channel when requested
- play = wantsRhythm;
- break;
- default:
- // Otherwise check for flag
- play = flags & hardwareMask;
- }
+ // In SCI1.1 and newer, the heap is appended at the end of the script,
+ // so adjust the offset accordingly
+ if (getSciVersion() >= SCI_VERSION_1_1 && addSci11ScriptOffset) {
+ offset += script->size;
- if (play) {
- // This Channel is supposed to be played by the hardware
- channelMask |= 0x8000;
- }
+ // Ensure that the start of the heap is word-aligned - same as in Script::init()
+ if (script->size & 2)
+ offset++;
}
- return channelMask;
+ return make_reg(1, offset);
}
-byte SoundResource::getInitialVoiceCount(byte channel) {
- byte *data = _innerResource->data;
+Common::String ResourceManager::findSierraGameId() {
+ // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated
+ Resource *heap = 0;
+ int nameSelector = 3;
- if (_soundVersion > SCI_VERSION_0_LATE)
- return 0; // TODO
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ heap = findResource(ResourceId(kResourceTypeScript, 0), false);
+ } else {
+ heap = findResource(ResourceId(kResourceTypeHeap, 0), false);
+ nameSelector += 5;
+ }
- data++; // Skip over digital sample flag
+ if (!heap)
+ return "";
- if (_soundVersion == SCI_VERSION_0_EARLY)
- return data[channel] >> 4;
- else
- return data[channel * 2];
+ int16 gameObjectOffset = findGameObject(false).offset;
+
+ if (!gameObjectOffset)
+ return "";
+
+ // Seek to the name selector of the first export
+ byte *seeker = heap->data + READ_UINT16(heap->data + gameObjectOffset + nameSelector * 2);
+ Common::String sierraId;
+ sierraId += (const char *)seeker;
+
+ return sierraId;
}
} // End of namespace Sci
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 48b5f095b1..48210b835f 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -23,21 +23,23 @@
*
*/
-#ifndef SCI_SCICORE_RESOURCE_H
-#define SCI_SCICORE_RESOURCE_H
+#ifndef SCI_RESOURCE_H
+#define SCI_RESOURCE_H
#include "common/str.h"
-#include "common/fs.h"
+#include "common/list.h"
+#include "common/hashmap.h"
#include "sci/graphics/helpers.h" // for ViewType
#include "sci/decompressor.h"
#include "sci/sci.h"
namespace Common {
-class ReadStream;
-class WriteStream;
class File;
+class FSList;
class FSNode;
+class WriteStream;
+class SeekableReadStream;
}
namespace Sci {
@@ -72,18 +74,6 @@ enum {
MAX_OPENED_VOLUMES = 5 ///< Max number of simultaneously opened volumes
};
-enum ResSourceType {
- kSourceDirectory = 0,
- kSourcePatch,
- kSourceVolume,
- kSourceExtMap,
- kSourceIntMap,
- kSourceAudioVolume,
- kSourceExtAudioMap,
- kSourceWave,
- kSourceMacResourceFork
-};
-
enum ResourceType {
kResourceTypeView = 0,
kResourceTypePic,
@@ -105,150 +95,221 @@ enum ResourceType {
kResourceTypeHeap,
kResourceTypeAudio36,
kResourceTypeSync36,
- kResourceTypeUnknown1, // Translation, currently unsupported
- kResourceTypeUnknown2,
+ kResourceTypeTranslation, // Currently unsupported
kResourceTypeRobot,
+ kResourceTypeVMD,
+ kResourceTypeChunk,
+
+ // Mac-only resources
+ kResourceTypeMacIconBarPictN, // IBIN resources (icon bar, not selected)
+ kResourceTypeMacIconBarPictS, // IBIS resources (icon bar, selected)
+ kResourceTypeMacPict, // PICT resources (inventory)
+
kResourceTypeInvalid
};
const char *getResourceTypeName(ResourceType restype);
+enum ResVersion {
+ kResVersionUnknown,
+ kResVersionSci0Sci1Early,
+ kResVersionSci1Middle,
+ kResVersionSci1Late,
+ kResVersionSci11,
+ kResVersionSci11Mac,
+ kResVersionSci32
+};
class ResourceManager;
-struct ResourceSource;
+class ResourceSource;
class ResourceId {
-public:
- ResourceType type;
- uint16 number;
- uint32 tuple; // Only used for audio36 and sync36
+ static inline ResourceType fixupType(ResourceType type) {
+ if (type >= kResourceTypeInvalid)
+ return kResourceTypeInvalid;
+ return type;
+ }
- ResourceId() : type(kResourceTypeInvalid), number(0), tuple(0) { }
+ ResourceType _type;
+ uint16 _number;
+ uint32 _tuple; // Only used for audio36 and sync36
+
+public:
+ ResourceId() : _type(kResourceTypeInvalid), _number(0), _tuple(0) { }
ResourceId(ResourceType type_, uint16 number_, uint32 tuple_ = 0)
- : type(type_), number(number_), tuple(tuple_) {
- if ((type < kResourceTypeView) || (type > kResourceTypeInvalid))
- type = kResourceTypeInvalid;
+ : _type(fixupType(type_)), _number(number_), _tuple(tuple_) {
}
ResourceId(ResourceType type_, uint16 number_, byte noun, byte verb, byte cond, byte seq)
- : type(type_), number(number_) {
- tuple = (noun << 24) | (verb << 16) | (cond << 8) | seq;
-
- if ((type < kResourceTypeView) || (type > kResourceTypeInvalid))
- type = kResourceTypeInvalid;
+ : _type(fixupType(type_)), _number(number_) {
+ _tuple = (noun << 24) | (verb << 16) | (cond << 8) | seq;
}
- Common::String toString() {
+ Common::String toString() const {
char buf[32];
- snprintf(buf, 32, "%s.%i", getResourceTypeName(type), number);
+ snprintf(buf, 32, "%s.%d", getResourceTypeName(_type), _number);
Common::String retStr = buf;
- if (tuple != 0) {
- snprintf(buf, 32, "(%i, %i, %i, %i)", tuple >> 24, (tuple >> 16) & 0xff, (tuple >> 8) & 0xff, tuple & 0xff);
+ if (_tuple != 0) {
+ snprintf(buf, 32, "(%d, %d, %d, %d)", _tuple >> 24, (_tuple >> 16) & 0xff, (_tuple >> 8) & 0xff, _tuple & 0xff);
retStr += buf;
}
return retStr;
}
-};
-struct ResourceIdHash : public Common::UnaryFunction<ResourceId, uint> {
- uint operator()(ResourceId val) const { return ((uint)((val.type << 16) | val.number)) ^ val.tuple; }
-};
+ inline ResourceType getType() const { return _type; }
+ inline uint16 getNumber() const { return _number; }
+ inline uint32 getTuple() const { return _tuple; }
-struct ResourceIdEqualTo : public Common::BinaryFunction<ResourceId, ResourceId, bool> {
- bool operator()(const ResourceId &x, const ResourceId &y) const { return (x.type == y.type) && (x.number == y.number) && (x.tuple == y.tuple); }
-};
+ inline uint hash() const {
+ return ((uint)((_type << 16) | _number)) ^ _tuple;
+ }
+
+ bool operator==(const ResourceId &other) const {
+ return (_type == other._type) && (_number == other._number) && (_tuple == other._tuple);
+ }
-struct ResourceIdLess : public Common::BinaryFunction<ResourceId, ResourceId, bool> {
- bool operator()(const ResourceId &x, const ResourceId &y) const {
- return (x.type < y.type) || ((x.type == y.type) && (x.number < y.number))
- || ((x.type == y.type) && (x.number == y.number) && (x.tuple < y.tuple));
+ bool operator<(const ResourceId &other) const {
+ return (_type < other._type) || ((_type == other._type) && (_number < other._number))
+ || ((_type == other._type) && (_number == other._number) && (_tuple < other._tuple));
}
};
+struct ResourceIdHash : public Common::UnaryFunction<ResourceId, uint> {
+ uint operator()(ResourceId val) const { return val.hash(); }
+};
+
/** Class for storing resources in memory */
class Resource {
friend class ResourceManager;
-public:
- Resource();
- ~Resource();
- void unalloc();
-// NOTE : Currently all member data has the same name and public visibility
-// to let the rest of the engine compile without changes
+ // FIXME: These 'friend' declarations are meant to be a temporary hack to
+ // ease transition to the ResourceSource class system.
+ friend class ResourceSource;
+ friend class PatchResourceSource;
+ friend class WaveResourceSource;
+ friend class AudioVolumeResourceSource;
+ friend class MacResourceForkResourceSource;
+#ifdef ENABLE_SCI32
+ friend class ChunkResourceSource;
+#endif
+
+// NOTE : Currently most member variables lack the underscore prefix and have
+// public visibility to let the rest of the engine compile without changes.
public:
- ResourceId _id;
byte *data;
uint32 size;
byte *_header;
uint32 _headerSize;
+public:
+ Resource(ResourceManager *resMan, ResourceId id);
+ ~Resource();
+ void unalloc();
+
+ inline ResourceType getType() const { return _id.getType(); }
+ inline uint16 getNumber() const { return _id.getNumber(); }
+ bool isLocked() const { return _status == kResStatusLocked; }
+ /**
+ * Write the resource to the specified stream.
+ * This method is used only by the "dump" debugger command.
+ */
void writeToStream(Common::WriteStream *stream) const;
- uint32 getAudioCompressionType();
+
+ // FIXME: This audio specific method is a hack. After all, why should a
+ // Resource have audio specific methods? But for now we keep this, as it
+ // eases transition.
+ uint32 getAudioCompressionType() const;
protected:
+ ResourceId _id; // TODO: _id could almost be made const, only readResourceInfo() modifies it...
int32 _fileOffset; /**< Offset in file */
ResourceStatus _status;
uint16 _lockers; /**< Number of places where this resource was locked */
ResourceSource *_source;
+ ResourceManager *_resMan;
+
+ bool loadPatch(Common::SeekableReadStream *file);
+ bool loadFromPatchFile();
+ bool loadFromWaveFile(Common::SeekableReadStream *file);
+ bool loadFromAudioVolumeSCI1(Common::SeekableReadStream *file);
+ bool loadFromAudioVolumeSCI11(Common::SeekableReadStream *file);
+ int decompress(ResVersion volVersion, Common::SeekableReadStream *file);
+ int readResourceInfo(ResVersion volVersion, Common::SeekableReadStream *file, uint32 &szPacked, ResourceCompression &compression);
};
-typedef Common::HashMap<ResourceId, Resource *, ResourceIdHash, ResourceIdEqualTo> ResourceMap;
+typedef Common::HashMap<ResourceId, Resource *, ResourceIdHash> ResourceMap;
class ResourceManager {
-public:
- enum ResVersion {
- kResVersionUnknown,
- kResVersionSci0Sci1Early,
- kResVersionSci1Middle,
- kResVersionSci1Late,
- kResVersionSci11,
- kResVersionSci11Mac,
- kResVersionSci32
- };
+ // FIXME: These 'friend' declarations are meant to be a temporary hack to
+ // ease transition to the ResourceSource class system.
+ friend class ResourceSource;
+ friend class DirectoryResourceSource;
+ friend class PatchResourceSource;
+ friend class ExtMapResourceSource;
+ friend class IntMapResourceSource;
+ friend class AudioVolumeResourceSource;
+ friend class ExtAudioMapResourceSource;
+ friend class WaveResourceSource;
+ friend class MacResourceForkResourceSource;
+#ifdef ENABLE_SCI32
+ friend class ChunkResourceSource;
+#endif
+public:
/**
* Creates a new SCI resource manager.
*/
ResourceManager();
- ResourceManager(const Common::FSList &fslist);
~ResourceManager();
+
+ /**
+ * Initializes the resource manager.
+ */
+ void init();
+
+ int addAppropriateSources();
+ int addAppropriateSources(const Common::FSList &fslist); // TODO: Switch from FSList to Common::Archive?
+
/**
* Looks up a resource's data.
- * @param id: The resource type to look for
- * @param lock: non-zero iff the resource should be locked
- * @return (Resource *): The resource, or NULL if it doesn't exist
+ * @param id The resource type to look for
+ * @param lock non-zero iff the resource should be locked
+ * @return The resource, or NULL if it doesn't exist
* @note Locked resources are guaranteed not to have their contents freed until
* they are unlocked explicitly (by unlockResource).
*/
Resource *findResource(ResourceId id, bool lock);
- /* Unlocks a previously locked resource
- ** (Resource *) res: The resource to free
- ** Returns : ()
- */
+ /**
+ * Unlocks a previously locked resource.
+ * @param res The resource to free
+ */
void unlockResource(Resource *res);
- /* Tests whether a resource exists
- ** (ResourceId) id: Id of the resource to check
- ** Returns : (Resource *) non-NULL if the resource exists, NULL otherwise
- ** This function may often be much faster than finding the resource
- ** and should be preferred for simple tests.
- ** The resource object returned is, indeed, the resource in question, but
- ** it should be used with care, as it may be unallocated.
- ** Use scir_find_resource() if you want to use the data contained in the resource.
- */
+ /**
+ * Tests whether a resource exists.
+ *
+ * This function may often be much faster than finding the resource
+ * and should be preferred for simple tests.
+ * The resource object returned is, indeed, the resource in question, but
+ * it should be used with care, as it may be unallocated.
+ * Use scir_find_resource() if you want to use the data contained in the resource.
+ *
+ * @param id Id of the resource to check
+ * @return non-NULL if the resource exists, NULL otherwise
+ */
Resource *testResource(ResourceId id);
/**
* Returns a list of all resources of the specified type.
- * @param type: The resource type to look for
- * @param mapNumber: For audio36 and sync36, limit search to this map
- * @return: The resource list
+ * @param type The resource type to look for
+ * @param mapNumber For audio36 and sync36, limit search to this map
+ * @return The resource list
*/
Common::List<ResourceId> *listResources(ResourceType type, int mapNumber = -1);
@@ -260,18 +321,49 @@ public:
ViewType getViewType() const { return _viewType; }
const char *getMapVersionDesc() const { return versionDescription(_mapVersion); }
const char *getVolVersionDesc() const { return versionDescription(_volVersion); }
+ ResVersion getVolVersion() const { return _volVersion; }
/**
* Adds the appropriate GM patch from the Sierra MIDI utility as 4.pat, without
* requiring the user to rename the file to 4.pat. Thus, the original Sierra
* archive can be extracted in the extras directory, and the GM patches can be
- * applied per game, if applicable
+ * applied per game, if applicable.
*/
- void addNewGMPatch(const Common::String &gameId);
+ void addNewGMPatch(SciGameId gameId);
+
+#ifdef ENABLE_SCI32
+ /**
+ * Parses all resources from a SCI2.1 chunk resource and adds them to the
+ * resource manager.
+ */
+ void addResourcesFromChunk(uint16 id);
+#endif
bool detectHires();
// Detects, if standard font of current game includes extended characters (>0x80)
bool detectFontExtended();
+ // Detects, if SCI1.1 game uses palette merging
+ bool detectForPaletteMergingForSci11();
+
+ /**
+ * Finds the internal Sierra ID of the current game from script 0.
+ */
+ Common::String findSierraGameId();
+
+ /**
+ * Finds the location of the game object from script 0.
+ * @param addSci11ScriptOffset Adjust the return value for SCI1.1 and newer
+ * games. Needs to be false when the heap is accessed directly inside
+ * findSierraGameId().
+ */
+ reg_t findGameObject(bool addSci11ScriptOffset = true);
+
+ /**
+ * Converts a map resource type to our type
+ * @param sciType The type from the map/patch
+ * @return The ResourceType
+ */
+ ResourceType convertResType(byte type);
protected:
// Maximum number of bytes to allow being allocated for resources
@@ -290,89 +382,58 @@ protected:
ResourceMap _resMap;
Common::List<Common::File *> _volumeFiles; ///< list of opened volume files
ResourceSource *_audioMapSCI1; ///< Currently loaded audio map for SCI1
- ResVersion _volVersion; ///< RESOURCE.0xx version
- ResVersion _mapVersion; ///< RESOURCE.MAP version
-
- /**
- * Initializes the resource manager
- */
- void init();
+ ResVersion _volVersion; ///< resource.0xx version
+ ResVersion _mapVersion; ///< resource.map version
/**
* Add a path to the resource manager's list of sources.
* @return a pointer to the added source structure, or NULL if an error occurred.
*/
- ResourceSource *addPatchDir(const char *path);
+ ResourceSource *addPatchDir(const Common::String &path);
- ResourceSource *getVolume(ResourceSource *map, int volume_nr);
+ ResourceSource *findVolume(ResourceSource *map, int volume_nr);
/**
* Adds a source to the resource manager's list of sources.
- * @param map The map associated with this source
- * @param type The source type
- * @param filename The name of the source to add
- * @return A pointer to the added source structure, or NULL if an error occurred.
+ * @param source The new source to add
+ * @return A pointer to the added source structure, or NULL if an error occurred.
*/
- ResourceSource *addSource(ResourceSource *map, ResSourceType type, const char *filename,
- int number);
-
- ResourceSource *addSource(ResourceSource *map, ResSourceType type,
- const Common::FSNode *resFile, int number);
+ ResourceSource *addSource(ResourceSource *source);
/**
- * Add an external (i.e., separate file) map resource to the resource manager's list of sources.
- * @param file_name The name of the volume to add
+ * Add an external (i.e., separate file) map resource to the resource
+ * manager's list of sources.
+ * @param filename The name of the volume to add
* @param volume_nr The volume number the map starts at, 0 for <SCI2.1
* @return A pointer to the added source structure, or NULL if an error occurred.
*/
- ResourceSource *addExternalMap(const char *file_name, int volume_nr = 0);
-
- ResourceSource *addExternalMap(const Common::FSNode *mapFile);
-
- /**
- * Add an internal (i.e., resource) map to the resource manager's list of sources.
- * @param name The name of the resource to add
- * @param resNr The map resource number
- * @return A pointer to the added source structure, or NULL if an error occurred.
- */
- ResourceSource *addInternalMap(const char *name, int resNr);
+ ResourceSource *addExternalMap(const Common::String &filename, int volume_nr = 0);
- /**
- * Checks, if an audio volume got compressed by our tool. If that's the case, it will set audioCompressionType
- * and read in the offset translation table for later usage.
- */
- void checkIfAudioVolumeIsCompressed(ResourceSource *source);
+ ResourceSource *addExternalMap(const Common::FSNode *mapFile, int volume_nr = 0);
/**
* Scans newly registered resource sources for resources, earliest addition first.
- * @param detected_version: Pointer to the detected version number,
+ * @param detected_version Pointer to the detected version number,
* used during startup. May be NULL.
* @return One of SCI_ERROR_*.
*/
void scanNewSources();
- int addAppropriateSources();
- int addAppropriateSources(const Common::FSList &fslist);
+
int addInternalSources();
void freeResourceSources();
/**
- * Returns a string describing a ResVersion
- * @param version: The resource version
- * @return: The description of version
+ * Returns a string describing a ResVersion.
+ * @param version The resource version
+ * @return The description of version
*/
const char *versionDescription(ResVersion version) const;
- Common::File *getVolumeFile(const char *filename);
+ Common::SeekableReadStream *getVolumeFile(ResourceSource *source);
void loadResource(Resource *res);
- bool loadPatch(Resource *res, Common::File &file);
- bool loadFromPatchFile(Resource *res);
- bool loadFromWaveFile(Resource *res, Common::File &file);
- bool loadFromAudioVolumeSCI1(Resource *res, Common::File &file);
- bool loadFromAudioVolumeSCI11(Resource *res, Common::File &file);
void freeOldResources();
- int decompress(Resource *res, Common::SeekableReadStream *file);
- int readResourceInfo(Resource *res, Common::SeekableReadStream *file, uint32&szPacked, ResourceCompression &compression);
void addResource(ResourceId resId, ResourceSource *src, uint32 offset, uint32 size = 0);
+ Resource *updateResource(ResourceId resId, ResourceSource *src, uint32 size);
void removeAudioResource(ResourceId resId);
/**--- Resource map decoding functions ---*/
@@ -392,23 +453,16 @@ protected:
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readResourceMapSCI1(ResourceSource *map);
-
- /**
- * Reads the SCI1.1+ resource file from a Mac resource fork.
- * @param source The source
- * @return 0 on success, an SCI_ERROR_* code otherwise
- */
- int readMacResourceFork(ResourceSource *source);
/**
- * Reads SCI1.1 audio map resources
+ * Reads SCI1.1 audio map resources.
* @param map The map
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readAudioMapSCI11(ResourceSource *map);
/**
- * Reads SCI1 audio map files
+ * Reads SCI1 audio map files.
* @param map The map
* @param unload Unload the map instead of loading it
* @return 0 on success, an SCI_ERROR_* code otherwise
@@ -420,13 +474,15 @@ protected:
/**
* Reads patch files from a local directory.
*/
- void readResourcePatches(ResourceSource *source);
- void processPatch(ResourceSource *source, ResourceType restype, int resnumber);
+ void readResourcePatches();
+ void readResourcePatchesBase36();
+ void processPatch(ResourceSource *source, ResourceType resourceType, uint16 resourceNr, uint32 tuple = 0);
/**
- * Process wave files as patches for Audio resources
+ * Process wave files as patches for Audio resources.
*/
void readWaveAudioPatches();
+ void processWavePatch(ResourceId resourceId, Common::String name);
/**
* Applies to all versions before 0.000.395 (i.e. KQ4 old, XMAS 1988 and LSL2).
@@ -457,6 +513,7 @@ public:
uint16 prio;
uint16 size;
byte *data;
+ uint16 curPos;
long time;
byte prev;
};
@@ -492,4 +549,4 @@ private:
} // End of namespace Sci
-#endif // SCI_SCICORE_RESOURCE_H
+#endif // SCI_RESOURCE_H
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
new file mode 100644
index 0000000000..a25505fe47
--- /dev/null
+++ b/engines/sci/resource_audio.cpp
@@ -0,0 +1,783 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 library
+
+#include "common/archive.h"
+#include "common/file.h"
+
+#include "sci/resource.h"
+#include "sci/resource_intern.h"
+#include "sci/util.h"
+
+namespace Sci {
+
+AudioVolumeResourceSource::AudioVolumeResourceSource(ResourceManager *resMan, const Common::String &name, ResourceSource *map, int volNum)
+ : VolumeResourceSource(name, map, volNum, kSourceAudioVolume) {
+
+ _audioCompressionType = 0;
+ _audioCompressionOffsetMapping = NULL;
+
+ /*
+ * Check if this audio volume got compressed by our tool. If that is the
+ * case, set _audioCompressionType and read in the offset translation
+ * table for later usage.
+ */
+
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, 0);
+ if (!fileStream)
+ return;
+
+ fileStream->seek(0, SEEK_SET);
+ uint32 compressionType = fileStream->readUint32BE();
+ switch (compressionType) {
+ case MKID_BE('MP3 '):
+ case MKID_BE('OGG '):
+ case MKID_BE('FLAC'):
+ // Detected a compressed audio volume
+ _audioCompressionType = compressionType;
+ // Now read the whole offset mapping table for later usage
+ int32 recordCount = fileStream->readUint32LE();
+ if (!recordCount)
+ error("compressed audio volume doesn't contain any entries!");
+ int32 *offsetMapping = new int32[(recordCount + 1) * 2];
+ _audioCompressionOffsetMapping = offsetMapping;
+ for (int recordNo = 0; recordNo < recordCount; recordNo++) {
+ *offsetMapping++ = fileStream->readUint32LE();
+ *offsetMapping++ = fileStream->readUint32LE();
+ }
+ // Put ending zero
+ *offsetMapping++ = 0;
+ *offsetMapping++ = fileStream->size();
+ }
+
+ if (_resourceFile)
+ delete fileStream;
+}
+
+bool Resource::loadFromWaveFile(Common::SeekableReadStream *file) {
+ data = new byte[size];
+
+ uint32 really_read = file->read(data, size);
+ if (really_read != size)
+ error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
+
+ _status = kResStatusAllocated;
+ return true;
+}
+
+bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) {
+ // Check for WAVE files here
+ uint32 riffTag = file->readUint32BE();
+ if (riffTag == MKID_BE('RIFF')) {
+ _headerSize = 0;
+ size = file->readUint32LE() + 8;
+ file->seek(-8, SEEK_CUR);
+ return loadFromWaveFile(file);
+ }
+ file->seek(-4, SEEK_CUR);
+
+ ResourceType type = _resMan->convertResType(file->readByte());
+ if (((getType() == kResourceTypeAudio || getType() == kResourceTypeAudio36) && (type != kResourceTypeAudio))
+ || ((getType() == kResourceTypeSync || getType() == kResourceTypeSync36) && (type != kResourceTypeSync))) {
+ warning("Resource type mismatch loading %s", _id.toString().c_str());
+ unalloc();
+ return false;
+ }
+
+ _headerSize = file->readByte();
+
+ if (type == kResourceTypeAudio) {
+ if (_headerSize != 7 && _headerSize != 11 && _headerSize != 12) {
+ warning("Unsupported audio header");
+ unalloc();
+ return false;
+ }
+
+ if (_headerSize != 7) { // Size is defined already from the map
+ // Load sample size
+ file->seek(7, SEEK_CUR);
+ size = file->readUint32LE();
+ // Adjust offset to point at the header data again
+ file->seek(-11, SEEK_CUR);
+ }
+ }
+
+ return loadPatch(file);
+}
+
+bool Resource::loadFromAudioVolumeSCI1(Common::SeekableReadStream *file) {
+ data = new byte[size];
+
+ if (data == NULL) {
+ error("Can't allocate %d bytes needed for loading %s", size, _id.toString().c_str());
+ }
+
+ unsigned int really_read = file->read(data, size);
+ if (really_read != size)
+ warning("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
+
+ _status = kResStatusAllocated;
+ return true;
+}
+
+void ResourceManager::addNewGMPatch(SciGameId gameId) {
+ Common::String gmPatchFile;
+
+ switch (gameId) {
+ case GID_ECOQUEST:
+ gmPatchFile = "ECO1GM.PAT";
+ break;
+ case GID_HOYLE3:
+ gmPatchFile = "HOY3GM.PAT";
+ break;
+ case GID_LSL1:
+ gmPatchFile = "LL1_GM.PAT";
+ break;
+ case GID_LSL5:
+ gmPatchFile = "LL5_GM.PAT";
+ break;
+ case GID_LONGBOW:
+ gmPatchFile = "ROBNGM.PAT";
+ break;
+ case GID_SQ1:
+ gmPatchFile = "SQ1_GM.PAT";
+ break;
+ case GID_SQ4:
+ gmPatchFile = "SQ4_GM.PAT";
+ break;
+ case GID_FAIRYTALES:
+ gmPatchFile = "TALEGM.PAT";
+ break;
+ default:
+ break;
+ }
+
+ if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
+ ResourceSource *psrcPatch = new PatchResourceSource(gmPatchFile);
+ processPatch(psrcPatch, kResourceTypePatch, 4);
+ }
+}
+
+void ResourceManager::processWavePatch(ResourceId resourceId, Common::String name) {
+ ResourceSource *resSrc = new WaveResourceSource(name);
+ Common::File file;
+ file.open(name);
+
+ updateResource(resourceId, resSrc, file.size());
+
+ debugC(1, kDebugLevelResMan, "Patching %s - OK", name.c_str());
+}
+
+void ResourceManager::readWaveAudioPatches() {
+ // Here we do check for SCI1.1+ so we can patch wav files in as audio resources
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, "*.wav");
+
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ Common::String name = (*x)->getName();
+
+ if (isdigit(name[0]))
+ processWavePatch(ResourceId(kResourceTypeAudio, atoi(name.c_str())), name);
+ }
+}
+
+void ResourceManager::removeAudioResource(ResourceId resId) {
+ // Remove resource, unless it was loaded from a patch
+ if (_resMap.contains(resId)) {
+ Resource *res = _resMap.getVal(resId);
+
+ if (res->_source->getSourceType() == kSourceAudioVolume) {
+ if (res->_status == kResStatusLocked) {
+ warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
+ } else {
+ if (res->_status == kResStatusEnqueued)
+ removeFromLRU(res);
+
+ _resMap.erase(resId);
+ delete res;
+ }
+ }
+ }
+}
+
+// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD):
+// =========
+// 6-byte entries:
+// w nEntry
+// dw offset
+
+// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX):
+// =========
+// 5-byte entries:
+// w nEntry
+// tb offset (cumulative)
+
+// QFG3 Demo 0.MAP structure:
+// =========
+// 10-byte entries:
+// w nEntry
+// dw offset
+// dw size
+
+// LB2 Floppy/Mother Goose SCI1.1 0.MAP structure:
+// =========
+// 8-byte entries:
+// w nEntry
+// w 0xffff
+// dw offset
+
+// Early SCI1.1 MAP structure:
+// ===============
+// 10-byte entries:
+// b noun
+// b verb
+// b cond
+// b seq
+// dw offset
+// w syncSize + syncAscSize
+
+// Late SCI1.1 MAP structure:
+// ===============
+// Header:
+// dw baseOffset
+// Followed by 7 or 11-byte entries:
+// b noun
+// b verb
+// b cond
+// b seq
+// tb cOffset (cumulative offset)
+// w syncSize (iff seq has bit 7 set)
+// w syncAscSize (iff seq has bit 6 set)
+
+int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
+ uint32 offset = 0;
+ Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_volumeNumber), false);
+
+ if (!mapRes) {
+ warning("Failed to open %i.MAP", map->_volumeNumber);
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ }
+
+ ResourceSource *src = findVolume(map, 0);
+
+ if (!src)
+ return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
+
+ byte *ptr = mapRes->data;
+
+ // Heuristic to detect entry size
+ uint32 entrySize = 0;
+ for (int i = mapRes->size - 1; i >= 0; --i) {
+ if (ptr[i] == 0xff)
+ entrySize++;
+ else
+ break;
+ }
+
+ if (map->_volumeNumber == 65535) {
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_LE_UINT16(ptr);
+ ptr += 2;
+
+ if (n == 0xffff)
+ break;
+
+ if (entrySize == 6) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ } else {
+ offset += READ_LE_UINT24(ptr);
+ ptr += 3;
+ }
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset);
+ }
+ } else if (map->_volumeNumber == 0 && entrySize == 10 && ptr[3] == 0) {
+ // QFG3 demo format
+ // ptr[3] would be 'seq' in the normal format and cannot possibly be 0
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ if (n == 0xffff)
+ break;
+
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ uint32 size = READ_LE_UINT32(ptr);
+ ptr += 4;
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ }
+ } else if (map->_volumeNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) {
+ // LB2 Floppy/Mother Goose SCI1.1 format
+ Common::SeekableReadStream *stream = getVolumeFile(src);
+
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_LE_UINT16(ptr);
+ ptr += 4;
+
+ if (n == 0xffff)
+ break;
+
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+
+ // The size is not stored in the map and the entries have no order.
+ // We need to dig into the audio resource in the volume to get the size.
+ stream->seek(offset + 1);
+ byte headerSize = stream->readByte();
+ assert(headerSize == 11 || headerSize == 12);
+
+ stream->skip(5);
+ uint32 size = stream->readUint32LE() + headerSize + 2;
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ }
+ } else {
+ bool isEarly = (entrySize != 11);
+
+ if (!isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ }
+
+ while (ptr < mapRes->data + mapRes->size) {
+ uint32 n = READ_BE_UINT32(ptr);
+ int syncSize = 0;
+ ptr += 4;
+
+ if (n == 0xffffffff)
+ break;
+
+ if (isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ } else {
+ offset += READ_LE_UINT24(ptr);
+ ptr += 3;
+ }
+
+ if (isEarly || (n & 0x80)) {
+ syncSize = READ_LE_UINT16(ptr);
+ ptr += 2;
+
+ if (syncSize > 0)
+ addResource(ResourceId(kResourceTypeSync36, map->_volumeNumber, n & 0xffffff3f), src, offset, syncSize);
+ }
+
+ if (n & 0x40) {
+ // This seems to define the size of raw lipsync data (at least
+ // in kq6), may also just be general appended data.
+ syncSize += READ_LE_UINT16(ptr);
+ ptr += 2;
+ }
+
+ addResource(ResourceId(kResourceTypeAudio36, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize);
+ }
+ }
+
+ return 0;
+}
+
+// AUDIOnnn.MAP contains 10-byte entries:
+// Early format:
+// w 5 bits resource type and 11 bits resource number
+// dw 7 bits volume number and 25 bits offset
+// dw size
+// Later format:
+// w nEntry
+// dw offset+volume (as in resource.map)
+// dw size
+// ending with 10 0xFFs
+int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
+ Common::File file;
+
+ if (!file.open(map->getLocationName()))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+
+ bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
+ file.seek(0);
+
+ while (1) {
+ uint16 n = file.readUint16LE();
+ uint32 offset = file.readUint32LE();
+ uint32 size = file.readUint32LE();
+
+ if (file.eos() || file.err()) {
+ warning("Error while reading %s", map->getLocationName().c_str());
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ }
+
+ if (n == 0xffff)
+ break;
+
+ byte volume_nr;
+
+ if (oldFormat) {
+ n &= 0x07ff; // Mask out resource type
+ volume_nr = offset >> 25; // most significant 7 bits
+ offset &= 0x01ffffff; // least significant 25 bits
+ } else {
+ volume_nr = offset >> 28; // most significant 4 bits
+ offset &= 0x0fffffff; // least significant 28 bits
+ }
+
+ ResourceSource *src = findVolume(map, volume_nr);
+
+ if (src) {
+ if (unload)
+ removeAudioResource(ResourceId(kResourceTypeAudio, n));
+ else
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ } else {
+ warning("Failed to find audio volume %i", volume_nr);
+ }
+ }
+
+ return 0;
+}
+
+void ResourceManager::setAudioLanguage(int language) {
+ if (_audioMapSCI1) {
+ if (_audioMapSCI1->_volumeNumber == language) {
+ // This language is already loaded
+ return;
+ }
+
+ // We already have a map loaded, so we unload it first
+ readAudioMapSCI1(_audioMapSCI1, true);
+
+ // Remove all volumes that use this map from the source list
+ Common::List<ResourceSource *>::iterator it = _sources.begin();
+ while (it != _sources.end()) {
+ ResourceSource *src = *it;
+ if (src->findVolume(_audioMapSCI1, src->_volumeNumber)) {
+ it = _sources.erase(it);
+ delete src;
+ } else {
+ ++it;
+ }
+ }
+
+ // Remove the map itself from the source list
+ _sources.remove(_audioMapSCI1);
+ delete _audioMapSCI1;
+
+ _audioMapSCI1 = NULL;
+ }
+
+ char filename[9];
+ snprintf(filename, 9, "AUDIO%03d", language);
+
+ Common::String fullname = Common::String(filename) + ".MAP";
+ if (!Common::File::exists(fullname)) {
+ warning("No audio map found for language %i", language);
+ return;
+ }
+
+ _audioMapSCI1 = addSource(new ExtAudioMapResourceSource(fullname, language));
+
+ // Search for audio volumes for this language and add them to the source list
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??");
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ const Common::String name = (*x)->getName();
+ const char *dot = strrchr(name.c_str(), '.');
+ int number = atoi(dot + 1);
+
+ addSource(new AudioVolumeResourceSource(this, name, _audioMapSCI1, number));
+ }
+
+ scanNewSources();
+}
+
+int ResourceManager::getAudioLanguage() const {
+ return (_audioMapSCI1 ? _audioMapSCI1->_volumeNumber : 0);
+}
+
+SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resourceNr), true);
+ int trackNr, channelNr;
+ if (!resource)
+ return;
+
+ _innerResource = resource;
+
+ byte *data, *data2;
+ byte *dataEnd;
+ Channel *channel, *sampleChannel;
+
+ switch (_soundVersion) {
+ case SCI_VERSION_0_EARLY:
+ case SCI_VERSION_0_LATE:
+ // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards
+ _trackCount = 1;
+ _tracks = new Track[_trackCount];
+ _tracks->digitalChannelNr = -1;
+ _tracks->type = 0; // Not used for SCI0
+ _tracks->channelCount = 1;
+ // Digital sample data included? -> Add an additional channel
+ if (resource->data[0] == 2)
+ _tracks->channelCount++;
+ _tracks->channels = new Channel[_tracks->channelCount];
+ memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount);
+ channel = &_tracks->channels[0];
+ if (_soundVersion == SCI_VERSION_0_EARLY) {
+ channel->data = resource->data + 0x11;
+ channel->size = resource->size - 0x11;
+ } else {
+ channel->data = resource->data + 0x21;
+ channel->size = resource->size - 0x21;
+ }
+ if (_tracks->channelCount == 2) {
+ // Digital sample data included
+ _tracks->digitalChannelNr = 1;
+ sampleChannel = &_tracks->channels[1];
+ // we need to find 0xFC (channel terminator) within the data
+ data = channel->data;
+ dataEnd = channel->data + channel->size;
+ while ((data < dataEnd) && (*data != 0xfc))
+ data++;
+ // Skip any following 0xFCs as well
+ while ((data < dataEnd) && (*data == 0xfc))
+ data++;
+ // Now adjust channels accordingly
+ sampleChannel->data = data;
+ sampleChannel->size = channel->size - (data - channel->data);
+ channel->size = data - channel->data;
+ // Read sample header information
+ //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer.
+ _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14);
+ _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32);
+ _tracks->digitalSampleStart = 0;
+ _tracks->digitalSampleEnd = 0;
+ sampleChannel->data += 44; // Skip over header
+ sampleChannel->size -= 44;
+ }
+ break;
+
+ case SCI_VERSION_1_EARLY:
+ case SCI_VERSION_1_LATE:
+ case SCI_VERSION_2_1:
+ data = resource->data;
+ // Count # of tracks
+ _trackCount = 0;
+ while ((*data++) != 0xFF) {
+ _trackCount++;
+ while (*data != 0xFF)
+ data += 6;
+ data++;
+ }
+ _tracks = new Track[_trackCount];
+ data = resource->data;
+
+ byte channelCount;
+
+ for (trackNr = 0; trackNr < _trackCount; trackNr++) {
+ // Track info starts with track type:BYTE
+ // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD
+ // 0xFF:BYTE as terminator to end that track and begin with another track type
+ // Track type 0xFF is the marker signifying the end of the tracks
+
+ _tracks[trackNr].type = *data++;
+ // Counting # of channels used
+ data2 = data;
+ channelCount = 0;
+ while (*data2 != 0xFF) {
+ data2 += 6;
+ channelCount++;
+ _tracks[trackNr].channelCount++;
+ }
+ _tracks[trackNr].channels = new Channel[channelCount];
+ _tracks[trackNr].channelCount = 0;
+ _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated
+ _tracks[trackNr].digitalSampleRate = 0;
+ _tracks[trackNr].digitalSampleSize = 0;
+ _tracks[trackNr].digitalSampleStart = 0;
+ _tracks[trackNr].digitalSampleEnd = 0;
+ if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently
+ channelNr = 0;
+ while (channelCount--) {
+ channel = &_tracks[trackNr].channels[channelNr];
+ channel->prio = READ_LE_UINT16(data);
+ uint dataOffset = READ_LE_UINT16(data + 2);
+ if (dataOffset < resource->size) {
+ channel->data = resource->data + dataOffset;
+ channel->size = READ_LE_UINT16(data + 4);
+ channel->curPos = 0;
+ channel->number = *channel->data;
+ channel->poly = *(channel->data + 1);
+ channel->time = channel->prev = 0;
+ channel->data += 2; // skip over header
+ channel->size -= 2; // remove header size
+ if (channel->number == 0xFE) { // Digital channel
+ _tracks[trackNr].digitalChannelNr = channelNr;
+ _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data);
+ _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2);
+ _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4);
+ _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6);
+ channel->data += 8; // Skip over header
+ channel->size -= 8;
+ }
+ _tracks[trackNr].channelCount++;
+ channelNr++;
+ } else {
+ warning("Invalid offset inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr);
+ }
+ data += 6;
+ }
+ } else {
+ // Skip over digital track
+ data += 6;
+ }
+ data++; // Skipping 0xFF that closes channels list
+ }
+ break;
+
+ default:
+ error("SoundResource: SCI version %d is unsupported", _soundVersion);
+ }
+}
+
+SoundResource::~SoundResource() {
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++)
+ delete[] _tracks[trackNr].channels;
+ delete[] _tracks;
+
+ _resMan->unlockResource(_innerResource);
+}
+
+#if 0
+SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) {
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return &_tracks[0];
+
+ if (/*number >= 0 &&*/number < _trackCount)
+ return &_tracks[number];
+ return NULL;
+}
+#endif
+
+SoundResource::Track *SoundResource::getTrackByType(byte type) {
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return &_tracks[0];
+
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
+ if (_tracks[trackNr].type == type)
+ return &_tracks[trackNr];
+ }
+ return NULL;
+}
+
+SoundResource::Track *SoundResource::getDigitalTrack() {
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
+ if (_tracks[trackNr].digitalChannelNr != -1)
+ return &_tracks[trackNr];
+ }
+ return NULL;
+}
+
+// Gets the filter mask for SCI0 sound resources
+int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
+ byte *data = _innerResource->data;
+ int channelMask = 0;
+
+ if (_soundVersion > SCI_VERSION_0_LATE)
+ return 0;
+
+ data++; // Skip over digital sample flag
+
+ for (int channelNr = 0; channelNr < 16; channelNr++) {
+ channelMask = channelMask >> 1;
+
+ byte flags;
+
+ if (_soundVersion == SCI_VERSION_0_EARLY) {
+ // Each channel is specified by a single byte
+ // Upper 4 bits of the byte is a voices count
+ // Lower 4 bits -> bit 0 set: use for AdLib
+ // bit 1 set: use for PCjr
+ // bit 2 set: use for PC speaker
+ // bit 3 set and bit 0 clear: control channel (15)
+ // bit 3 set and bit 0 set: rhythm channel (9)
+ // Note: control channel is dynamically assigned inside the drivers,
+ // but seems to be fixed at 15 in the song data.
+ flags = *data++;
+
+ // Get device bits
+ flags &= 0x7;
+ } else {
+ // Each channel is specified by 2 bytes
+ // 1st byte is voices count
+ // 2nd byte is play mask, which specifies if the channel is supposed to be played
+ // by the corresponding hardware
+
+ // Skip voice count
+ data++;
+
+ flags = *data++;
+ }
+
+ bool play;
+ switch (channelNr) {
+ case 15:
+ // Always play control channel
+ play = true;
+ break;
+ case 9:
+ // Play rhythm channel when requested
+ play = wantsRhythm;
+ break;
+ default:
+ // Otherwise check for flag
+ play = flags & hardwareMask;
+ }
+
+ if (play) {
+ // This Channel is supposed to be played by the hardware
+ channelMask |= 0x8000;
+ }
+ }
+
+ return channelMask;
+}
+
+byte SoundResource::getInitialVoiceCount(byte channel) {
+ byte *data = _innerResource->data;
+
+ if (_soundVersion > SCI_VERSION_0_LATE)
+ return 0; // TODO
+
+ data++; // Skip over digital sample flag
+
+ if (_soundVersion == SCI_VERSION_0_EARLY)
+ return data[channel] >> 4;
+ else
+ return data[channel * 2];
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h
new file mode 100644
index 0000000000..14f872b46e
--- /dev/null
+++ b/engines/sci/resource_intern.h
@@ -0,0 +1,219 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 SCI_RESOURCE_INTERN_H
+#define SCI_RESOURCE_INTERN_H
+
+#include "sci/resource.h"
+
+namespace Common {
+ class MacResManager;
+}
+
+namespace Sci {
+
+enum ResSourceType {
+ kSourceDirectory = 0,
+ kSourcePatch,
+ kSourceVolume,
+ kSourceExtMap,
+ kSourceIntMap,
+ kSourceAudioVolume,
+ kSourceExtAudioMap,
+ kSourceWave,
+ kSourceMacResourceFork,
+ kSourceChunk
+};
+
+
+class ResourceSource {
+protected:
+ const ResSourceType _sourceType;
+ const Common::String _name;
+
+public:
+ bool _scanned;
+ const Common::FSNode * const _resourceFile;
+ const int _volumeNumber;
+
+protected:
+ ResourceSource(ResSourceType type, const Common::String &name, int volNum = 0, const Common::FSNode *resFile = 0);
+public:
+ virtual ~ResourceSource();
+
+ ResSourceType getSourceType() const { return _sourceType; }
+ const Common::String &getLocationName() const { return _name; }
+
+ // Auxiliary method, used by loadResource implementations.
+ Common::SeekableReadStream *getVolumeFile(ResourceManager *resMan, Resource *res);
+
+ /**
+ * TODO: Document this
+ */
+ virtual ResourceSource *findVolume(ResourceSource *map, int volNum) {
+ return NULL;
+ }
+
+ /**
+ * Scan this source for TODO.
+ */
+ virtual void scanSource(ResourceManager *resMan) {}
+
+ /**
+ * Load a resource.
+ */
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+
+ // FIXME: This audio specific method is a hack. After all, why should a
+ // ResourceSource or a Resource (which uses this method) have audio
+ // specific methods? But for now we keep this, as it eases transition.
+ virtual uint32 getAudioCompressionType() const { return 0; }
+};
+
+class DirectoryResourceSource : public ResourceSource {
+public:
+ DirectoryResourceSource(const Common::String &name) : ResourceSource(kSourceDirectory, name) {}
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class PatchResourceSource : public ResourceSource {
+public:
+ PatchResourceSource(const Common::String &name) : ResourceSource(kSourcePatch, name) {}
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+};
+
+class VolumeResourceSource : public ResourceSource {
+protected:
+ ResourceSource * const _associatedMap;
+
+public:
+ VolumeResourceSource(const Common::String &name, ResourceSource *map, int volNum, ResSourceType type = kSourceVolume)
+ : ResourceSource(type, name, volNum), _associatedMap(map) {
+ }
+
+ VolumeResourceSource(const Common::String &name, ResourceSource *map, int volNum, const Common::FSNode *resFile)
+ : ResourceSource(kSourceVolume, name, volNum, resFile), _associatedMap(map) {
+ }
+
+ virtual ResourceSource *findVolume(ResourceSource *map, int volNum) {
+ if (_associatedMap == map && _volumeNumber == volNum)
+ return this;
+ return NULL;
+ }
+};
+
+class ExtMapResourceSource : public ResourceSource {
+public:
+ ExtMapResourceSource(const Common::String &name, int volNum, const Common::FSNode *resFile = 0)
+ : ResourceSource(kSourceExtMap, name, volNum, resFile) {
+ }
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class IntMapResourceSource : public ResourceSource {
+public:
+ IntMapResourceSource(const Common::String &name, int volNum)
+ : ResourceSource(kSourceIntMap, name, volNum) {
+ }
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class AudioVolumeResourceSource : public VolumeResourceSource {
+protected:
+ uint32 _audioCompressionType;
+ int32 *_audioCompressionOffsetMapping;
+
+public:
+ AudioVolumeResourceSource(ResourceManager *resMan, const Common::String &name, ResourceSource *map, int volNum);
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+
+ virtual uint32 getAudioCompressionType() const;
+};
+
+class ExtAudioMapResourceSource : public ResourceSource {
+public:
+ ExtAudioMapResourceSource(const Common::String &name, int volNum)
+ : ResourceSource(kSourceExtAudioMap, name, volNum) {
+ }
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class WaveResourceSource : public ResourceSource {
+public:
+ WaveResourceSource(const Common::String &name) : ResourceSource(kSourceWave, name) {}
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+};
+
+/**
+ * Reads SCI1.1+ resources from a Mac resource fork.
+ */
+class MacResourceForkResourceSource : public ResourceSource {
+protected:
+ Common::MacResManager *_macResMan;
+
+public:
+ MacResourceForkResourceSource(const Common::String &name, int volNum);
+ ~MacResourceForkResourceSource();
+
+ virtual void scanSource(ResourceManager *resMan);
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+};
+
+#ifdef ENABLE_SCI32
+
+/**
+ * Reads resources from SCI2.1+ chunk resources
+ */
+class ChunkResourceSource : public ResourceSource {
+public:
+ ChunkResourceSource(const Common::String &name, uint16 number);
+
+ virtual void scanSource(ResourceManager *resMan);
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+
+protected:
+ uint16 _number;
+
+ struct ResourceEntry {
+ uint32 offset;
+ uint32 length;
+ };
+
+ Common::HashMap<ResourceId, ResourceEntry, ResourceIdHash> _resMap;
+};
+
+#endif
+
+} // End of namespace Sci
+
+#endif // SCI_RESOURCE_INTERN_H
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 4862d0579a..7a9a786121 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -26,6 +26,8 @@
#include "common/system.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
+#include "common/EventRecorder.h"
+#include "common/file.h" // for Common::File::exists()
#include "engines/advancedDetector.h"
#include "engines/util.h"
@@ -36,39 +38,59 @@
#include "sci/event.h"
#include "sci/engine/features.h"
+#include "sci/engine/message.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/script.h" // for script_adjust_opcode_formats
+#include "sci/engine/selector.h" // for SELECTOR
#include "sci/sound/audio.h"
#include "sci/sound/soundcmd.h"
-#include "sci/graphics/gui.h"
+#include "sci/graphics/animate.h"
+#include "sci/graphics/cache.h"
+#include "sci/graphics/compare.h"
+#include "sci/graphics/controls.h"
+#include "sci/graphics/coordadjuster.h"
+#include "sci/graphics/cursor.h"
+#include "sci/graphics/maciconbar.h"
+#include "sci/graphics/menu.h"
+#include "sci/graphics/paint16.h"
+#include "sci/graphics/paint32.h"
+#include "sci/graphics/picture.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/palette.h"
-#include "sci/graphics/cursor.h"
#include "sci/graphics/screen.h"
-#include "sci/graphics/cache.h"
+#include "sci/graphics/text16.h"
+#include "sci/graphics/transitions.h"
#ifdef ENABLE_SCI32
-#include "sci/graphics/gui32.h"
+#include "sci/graphics/frameout.h"
#endif
namespace Sci {
-extern int g_loadFromLauncher;
-
SciEngine *g_sci = 0;
class GfxDriver;
-SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc)
- : Engine(syst), _gameDescription(desc), _system(syst) {
- _console = NULL;
+SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId)
+ : Engine(syst), _gameDescription(desc), _gameId(gameId) {
assert(g_sci == 0);
g_sci = this;
+
+ _gfxMacIconBar = 0;
+
+ _audio = 0;
_features = 0;
+ _resMan = 0;
+ _gamestate = 0;
+ _kernel = 0;
+ _vocabulary = 0;
+ _vocabularyLanguage = 1; // we load english vocabulary on startup
+ _eventMan = 0;
+ _console = 0;
// Set up the engine specific debug levels
DebugMan.addDebugChannel(kDebugLevelError, "Error", "Script error debugging");
@@ -79,10 +101,8 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc)
DebugMan.addDebugChannel(kDebugLevelFuncCheck, "Func", "Function parameter debugging");
DebugMan.addDebugChannel(kDebugLevelBresen, "Bresenham", "Bresenham algorithms debugging");
DebugMan.addDebugChannel(kDebugLevelSound, "Sound", "Sound debugging");
- DebugMan.addDebugChannel(kDebugLevelGfxDriver, "Gfxdriver", "Gfx driver debugging");
DebugMan.addDebugChannel(kDebugLevelBaseSetter, "Base", "Base Setter debugging");
DebugMan.addDebugChannel(kDebugLevelParser, "Parser", "Parser debugging");
- DebugMan.addDebugChannel(kDebugLevelMenu, "Menu", "Menu handling debugging");
DebugMan.addDebugChannel(kDebugLevelSaid, "Said", "Said specs debugging");
DebugMan.addDebugChannel(kDebugLevelFile, "File", "File I/O debugging");
DebugMan.addDebugChannel(kDebugLevelTime, "Time", "Time debugging");
@@ -92,25 +112,28 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc)
DebugMan.addDebugChannel(kDebugLevelVM, "VM", "VM debugging");
DebugMan.addDebugChannel(kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded");
DebugMan.addDebugChannel(kDebugLevelGC, "GC", "Garbage Collector debugging");
- DebugMan.addDebugChannel(kDebugLevelSci0Pic, "Sci0Pic", "SCI0 pic drawing debugging");
DebugMan.addDebugChannel(kDebugLevelResMan, "ResMan", "Resource manager debugging");
DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game");
- _gamestate = 0;
-
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "actors"); // KQ6 hi-res portraits
SearchMan.addSubDirectoryMatching(gameDataDir, "aud"); // resource.aud and audio files
- SearchMan.addSubDirectoryMatching(gameDataDir, "avi"); // AVI movie files for Windows versions
- SearchMan.addSubDirectoryMatching(gameDataDir, "seq"); // SEQ movie files for DOS versions
+ SearchMan.addSubDirectoryMatching(gameDataDir, "audio");// resource.aud and audio files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "audiosfx");// resource.aud and audio files
SearchMan.addSubDirectoryMatching(gameDataDir, "wav"); // speech files in WAV format
SearchMan.addSubDirectoryMatching(gameDataDir, "sfx"); // music/sound files in WAV format
- SearchMan.addSubDirectoryMatching(gameDataDir, "robot"); // robot files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "avi"); // AVI movie files for Windows versions
+ SearchMan.addSubDirectoryMatching(gameDataDir, "seq"); // SEQ movie files for DOS versions
+ SearchMan.addSubDirectoryMatching(gameDataDir, "robot"); // robot movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "robots"); // robot movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "movie"); // vmd movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "movies"); // vmd movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "vmd"); // vmd movie files
// Add the patches directory, except for KQ6CD; The patches folder in some versions of KQ6CD
// is for the demo of Phantasmagoria, included in the disk
- if (strcmp(getGameID(), "kq6"))
+ if (_gameId != GID_KQ6)
SearchMan.addSubDirectoryMatching(gameDataDir, "patches"); // resource patches
}
@@ -118,184 +141,352 @@ SciEngine::~SciEngine() {
// Remove all of our debug levels here
DebugMan.clearAllDebugChannels();
+#ifdef ENABLE_SCI32
+ delete _gfxFrameout;
+#endif
+ delete _gfxMenu;
+ delete _gfxControls;
+ delete _gfxText16;
+ delete _gfxAnimate;
+ delete _gfxPaint;
+ delete _gfxTransitions;
+ delete _gfxCompare;
+ delete _gfxCoordAdjuster;
+ delete _gfxPorts;
+ delete _gfxCache;
+ delete _gfxPalette;
+ delete _gfxCursor;
+ delete _gfxScreen;
+
delete _audio;
+ delete _soundCmd;
delete _kernel;
delete _vocabulary;
delete _console;
- delete _resMan;
delete _features;
+ delete _gfxMacIconBar;
+ delete _eventMan;
+ delete _gamestate->_segMan;
+ delete _gamestate;
+ delete _resMan; // should be deleted last
g_sci = 0;
}
Common::Error SciEngine::run() {
+ g_eventRec.registerRandomSource(_rng, "sci");
+
// Assign default values to the config manager, in case settings are missing
ConfMan.registerDefault("undither", "true");
ConfMan.registerDefault("enable_fb01", "false");
_resMan = new ResourceManager();
+ assert(_resMan);
+ _resMan->addAppropriateSources();
+ _resMan->init();
+ // TODO: Add error handling. Check return values of addAppropriateSources
+ // and init. We first have to *add* sensible return values, though ;).
+/*
if (!_resMan) {
warning("No resources found, aborting");
return Common::kNoGameDataFoundError;
}
+*/
+
+ // Reset, so that error()s before SoundCommandParser is initialized wont cause a crash
+ _soundCmd = NULL;
+
+ // Add the after market GM patches for the specified game, if they exist
+ _resMan->addNewGMPatch(_gameId);
+ _gameObj = _resMan->findGameObject();
SegManager *segMan = new SegManager(_resMan);
- // Scale the screen, if needed
- int upscaledHires = GFX_SCREEN_UPSCALED_DISABLED;
+ // Initialize the game screen
+ _gfxScreen = new GfxScreen(_resMan);
+ _gfxScreen->debugUnditherSetState(ConfMan.getBool("undither"));
- // King's Quest 6 and Gabriel Knight 1 have hires content, gk1/cd was able to provide that under DOS as well, but as
- // gk1/floppy does support upscaled hires scriptswise, but doesn't actually have the hires content we need to limit
- // it to platform windows.
- if (getPlatform() == Common::kPlatformWindows) {
- if (!strcmp(getGameID(), "kq6"))
- upscaledHires = GFX_SCREEN_UPSCALED_640x440;
-#ifdef ENABLE_SCI32
- if (!strcmp(getGameID(), "gk1"))
- upscaledHires = GFX_SCREEN_UPSCALED_640x480;
-#endif
+ // Create debugger console. It requires GFX to be initialized
+ _console = new Console(this);
+ _kernel = new Kernel(_resMan, segMan);
+ _features = new GameFeatures(segMan, _kernel);
+ // Only SCI0, SCI01 and SCI1 EGA games used a parser
+ _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan, false) : NULL;
+ // Also, XMAS1990 apparently had a parser too. Refer to http://forums.scummvm.org/viewtopic.php?t=9135
+ if (getGameId() == GID_CHRISTMAS1990)
+ _vocabulary = new Vocabulary(_resMan, false);
+ _audio = new AudioPlayer(_resMan);
+ _gamestate = new EngineState(segMan);
+ _eventMan = new EventManager(_resMan->detectFontExtended());
+
+ // The game needs to be initialized before the graphics system is initialized, as
+ // the graphics code checks parts of the seg manager upon initialization (e.g. for
+ // the presence of the fastCast object)
+ if (!initGame()) { /* Initialize */
+ warning("Game initialization failed: Aborting...");
+ // TODO: Add an "init failed" error?
+ return Common::kUnknownError;
}
- // Japanese versions of games use hi-res font on upscaled version of the game
- if ((getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
- upscaledHires = GFX_SCREEN_UPSCALED_640x400;
+ script_adjust_opcode_formats();
- // Initialize graphics-related parts
- GfxScreen *screen = 0;
+ // Must be called after game_init(), as they use _features
+ _kernel->loadKernelNames(_features);
+ _soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, _features->detectDoSoundType());
- // invokes initGraphics()
- if (_resMan->detectHires())
- screen = new GfxScreen(_resMan, 640, 480);
- else
- screen = new GfxScreen(_resMan, 320, 200, upscaledHires);
+ syncSoundSettings();
- GfxPalette *palette = new GfxPalette(_resMan, screen);
- GfxCache *cache = new GfxCache(_resMan, screen, palette);
- GfxCursor *cursor = new GfxCursor(_resMan, palette, screen);
+ // Initialize all graphics related subsystems
+ initGraphics();
- // Create debugger console. It requires GFX to be initialized
- _console = new Console(this);
+ debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()));
- _kernel = new Kernel(_resMan, segMan);
- // Only SCI0 and SCI01 games used a parser
- _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan) : NULL;
- _audio = new AudioPlayer(_resMan);
+ if (_gameDescription->flags & ADGF_ADDENGLISH) {
+ // if game is multilingual
+ Common::Language selectedLanguage = Common::parseLanguage(ConfMan.get("language"));
+ if (selectedLanguage == Common::EN_ANY) {
+ // and english was selected as language
+ if (SELECTOR(printLang) != -1) // set text language to english
+ writeSelectorValue(segMan, _gameObj, SELECTOR(printLang), 1);
+ if (SELECTOR(parseLang) != -1) // and set parser language to english as well
+ writeSelectorValue(segMan, _gameObj, SELECTOR(parseLang), 1);
+ }
+ }
- _features = new GameFeatures(segMan, _kernel);
+ // Check whether loading a savestate was requested
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (saveSlot >= 0) {
+ reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL)
+ kRestoreGame(_gamestate, 2, restoreArgv);
+
+ // TODO: The best way to do the following would be to invoke Game::init
+ // here and stop when the room is about to be changed, otherwise some
+ // game initialization won't take place
+
+ // Set audio language for KQ5CD (bug #3039477)
+ if (g_sci->getGameId() == GID_KQ5 && Common::File::exists("AUDIO001.002")) {
+ reg_t doAudioArgv[2] = { make_reg(0, 9), make_reg(0, 1) };
+ kDoAudio(_gamestate, 2, doAudioArgv);
+ }
+
+ // Initialize the game menu, if there is one.
+ // This is not done when loading, so we must do it manually.
+ reg_t menuBarObj = _gamestate->_segMan->findObjectByName("MenuBar");
+ if (menuBarObj.isNull())
+ menuBarObj = _gamestate->_segMan->findObjectByName("TheMenuBar"); // LSL2
+ if (menuBarObj.isNull())
+ menuBarObj = _gamestate->_segMan->findObjectByName("menuBar"); // LSL6
+ if (!menuBarObj.isNull()) {
+ // Reset abortScriptProcessing before initializing the game menu, so that the
+ // VM call performed by invokeSelector will actually run.
+ _gamestate->abortScriptProcessing = kAbortNone;
+ Object *menuBar = _gamestate->_segMan->getObject(menuBarObj);
+ // Invoke the first method (init) of the menuBar object
+ invokeSelector(_gamestate, menuBarObj, menuBar->getFuncSelector(0), 0, _gamestate->stack_base);
+ _gamestate->abortScriptProcessing = kAbortLoadGame;
+ }
+ }
- _gamestate = new EngineState(_vocabulary, segMan);
+ runGame();
- _gamestate->_event = new SciEvent(_resMan);
+ ConfMan.flushToDisk();
- if (script_init_engine(_gamestate))
- return Common::kUnknownError;
+ return Common::kNoError;
+}
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2) {
- _gfxAnimate = 0;
- _gfxControls = 0;
- _gfxMenu = 0;
- _gfxPaint16 = 0;
- _gfxPorts = 0;
- _gui = 0;
- _gui32 = new SciGui32(_gamestate->_segMan, _gamestate->_event, screen, palette, cache, cursor);
- } else {
-#endif
- _gfxPorts = new GfxPorts(segMan, screen);
- _gui = new SciGui(_gamestate, screen, palette, cache, cursor, _gfxPorts, _audio);
-#ifdef ENABLE_SCI32
- _gui32 = 0;
- _gfxFrameout = 0;
- }
-#endif
+bool SciEngine::initGame() {
+ // Script 0 needs to be allocated here before anything else!
+ int script0Segment = _gamestate->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK);
+ DataStack *stack = _gamestate->_segMan->allocateStack(VM_STACK_SIZE, NULL);
- _gfxPalette = palette;
- _gfxScreen = screen;
- _gfxCache = cache;
- _gfxCursor = cursor;
+ _gamestate->_msgState = new MessageState(_gamestate->_segMan);
+ _gamestate->gcCountDown = GC_INTERVAL - 1;
- if (game_init(_gamestate)) { /* Initialize */
- warning("Game initialization failed: Aborting...");
- // TODO: Add an "init failed" error?
- return Common::kUnknownError;
+ // Script 0 should always be at segment 1
+ if (script0Segment != 1) {
+ debug(2, "Failed to instantiate script.000");
+ return false;
}
- // Add the after market GM patches for the specified game, if they exist
- _resMan->addNewGMPatch(_gamestate->_gameId);
+ _gamestate->initGlobals();
+ _gamestate->_segMan->initSysStrings();
- script_adjust_opcode_formats(_gamestate);
- _kernel->loadKernelNames(getGameID());
+ _gamestate->r_acc = _gamestate->r_prev = NULL_REG;
- // Set the savegame dir (actually, we set it to a fake value,
- // since we cannot let the game control where saves are stored)
- assert(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value != 0);
- strcpy(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value, "");
+ _gamestate->_executionStack.clear(); // Start without any execution stack
+ _gamestate->executionStackBase = -1; // No vm is running yet
+ _gamestate->_executionStackPosChanged = false;
- SciVersion soundVersion = _features->detectDoSoundType();
+ _gamestate->abortScriptProcessing = kAbortNone;
+ _gamestate->gameIsRestarting = GAMEISRESTARTING_NONE;
- _gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion);
+ _gamestate->stack_base = stack->_entries;
+ _gamestate->stack_top = stack->_entries + stack->_capacity;
- screen->debugUnditherSetState(ConfMan.getBool("undither"));
+ if (!_gamestate->_segMan->instantiateScript(0)) {
+ error("initGame(): Could not instantiate script 0");
+ return false;
+ }
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (game_init_sound(_gamestate, 0, soundVersion)) {
- warning("Game initialization failed: Error in sound subsystem. Aborting...");
- return Common::kUnknownError;
+ // Reset parser
+ if (_vocabulary) {
+ _vocabulary->reset();
}
-#endif
- syncSoundSettings();
+ _gamestate->gameStartTime = _gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis();
+
+ // Load game language into printLang property of game object
+ setSciLanguage();
+
+ return true;
+}
+void SciEngine::initGraphics() {
+
+ // Reset all graphics objects
+ _gfxAnimate = 0;
+ _gfxCache = 0;
+ _gfxCompare = 0;
+ _gfxControls = 0;
+ _gfxCoordAdjuster = 0;
+ _gfxCursor = 0;
+ _gfxMacIconBar = 0;
+ _gfxMenu = 0;
+ _gfxPaint = 0;
+ _gfxPaint16 = 0;
+ _gfxPalette = 0;
+ _gfxPorts = 0;
+ _gfxText16 = 0;
+ _gfxTransitions = 0;
#ifdef ENABLE_SCI32
- if (_gui32)
- _gui32->init();
- else
+ _gfxFrameout = 0;
+ _gfxPaint32 = 0;
#endif
- _gui->init(_features->usesOldGfxFunctions());
- debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()));
+ if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1)
+ _gfxMacIconBar = new GfxMacIconBar();
- // Check whether loading a savestate was requested
- if (ConfMan.hasKey("save_slot")) {
- g_loadFromLauncher = ConfMan.getInt("save_slot");
+ bool paletteMerging = true;
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ // there are some games that use inbetween SCI1.1 interpreter, so we have to detect if it's merging or copying
+ if (getSciVersion() == SCI_VERSION_1_1)
+ paletteMerging = _resMan->detectForPaletteMergingForSci11();
+ else
+ paletteMerging = false;
+ }
+
+ _gfxPalette = new GfxPalette(_resMan, _gfxScreen, paletteMerging);
+ _gfxCache = new GfxCache(_resMan, _gfxScreen, _gfxPalette);
+ _gfxCursor = new GfxCursor(_resMan, _gfxPalette, _gfxScreen);
+
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ // SCI32 graphic objects creation
+ _gfxCoordAdjuster = new GfxCoordAdjuster32(_gamestate->_segMan);
+ _gfxCursor->init(_gfxCoordAdjuster, _eventMan);
+ _gfxCompare = new GfxCompare(_gamestate->_segMan, g_sci->getKernel(), _gfxCache, _gfxScreen, _gfxCoordAdjuster);
+ _gfxPaint32 = new GfxPaint32(g_sci->getResMan(), _gamestate->_segMan, g_sci->getKernel(), _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette);
+ _gfxPaint = _gfxPaint32;
+ _gfxFrameout = new GfxFrameout(_gamestate->_segMan, g_sci->getResMan(), _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32);
} else {
- g_loadFromLauncher = -1;
+#endif
+ // SCI0-SCI1.1 graphic objects creation
+ _gfxPorts = new GfxPorts(_gamestate->_segMan, _gfxScreen);
+ _gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts);
+ _gfxCursor->init(_gfxCoordAdjuster, g_sci->getEventManager());
+ _gfxCompare = new GfxCompare(_gamestate->_segMan, g_sci->getKernel(), _gfxCache, _gfxScreen, _gfxCoordAdjuster);
+ _gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette, g_sci->getResMan()->isVGA());
+ _gfxPaint16 = new GfxPaint16(g_sci->getResMan(), _gamestate->_segMan, g_sci->getKernel(), _gfxCache, _gfxPorts, _gfxCoordAdjuster, _gfxScreen, _gfxPalette, _gfxTransitions, _audio);
+ _gfxPaint = _gfxPaint16;
+ _gfxAnimate = new GfxAnimate(_gamestate, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette, _gfxCursor, _gfxTransitions);
+ _gfxText16 = new GfxText16(g_sci->getResMan(), _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen);
+ _gfxControls = new GfxControls(_gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen);
+ _gfxMenu = new GfxMenu(g_sci->getEventManager(), _gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen, _gfxCursor);
+
+ _gfxMenu->reset();
+#ifdef ENABLE_SCI32
}
+#endif
- game_run(&_gamestate); // Run the game
+ if (_gfxPorts) {
+ _gfxPorts->init(_features->usesOldGfxFunctions(), _gfxPaint16, _gfxText16);
+ _gfxPaint16->init(_gfxAnimate, _gfxText16);
+ }
+ // Set default (EGA, amiga or resource 999) palette
+ _gfxPalette->setDefault();
+}
- game_exit(_gamestate);
+void SciEngine::initStackBaseWithSelector(Selector selector) {
+ _gamestate->stack_base[0] = make_reg(0, (uint16)selector);
+ _gamestate->stack_base[1] = NULL_REG;
- ConfMan.flushToDisk();
+ // Register the first element on the execution stack
+ if (!send_selector(_gamestate, _gameObj, _gameObj, _gamestate->stack_base, 2, _gamestate->stack_base)) {
+ _console->printObject(_gameObj);
+ error("initStackBaseWithSelector: error while registering the first selector in the call stack");
+ }
- delete _gamestate->_soundCmd;
- delete _gui;
-#ifdef ENABLE_SCI32
- delete _gui32;
-#endif
- delete _gfxPorts;
- delete _gfxCache;
- delete _gfxPalette;
- delete cursor;
- delete _gfxScreen;
- delete _gamestate->_event;
- delete segMan;
- delete _gamestate;
+}
- return Common::kNoError;
+void SciEngine::runGame() {
+ initStackBaseWithSelector(SELECTOR(play)); // Call the play selector
+
+ // Attach the debug console on game startup, if requested
+ if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup))
+ _console->attach();
+
+ do {
+ _gamestate->_executionStackPosChanged = false;
+ run_vm(_gamestate);
+ exitGame();
+
+ if (_gamestate->abortScriptProcessing == kAbortRestartGame) {
+ _gamestate->_segMan->resetSegMan();
+ initGame();
+ initStackBaseWithSelector(SELECTOR(play));
+ _gamestate->gameIsRestarting = GAMEISRESTARTING_RESTART;
+ if (_gfxMenu)
+ _gfxMenu->reset();
+ _gamestate->abortScriptProcessing = kAbortNone;
+ } else if (_gamestate->abortScriptProcessing == kAbortLoadGame) {
+ _gamestate->abortScriptProcessing = kAbortNone;
+ _gamestate->_executionStack.clear();
+ initStackBaseWithSelector(SELECTOR(replay));
+ _gamestate->shrinkStackToBase();
+ _gamestate->abortScriptProcessing = kAbortNone;
+ } else {
+ break; // exit loop
+ }
+ } while (true);
+}
+
+void SciEngine::exitGame() {
+ if (_gamestate->abortScriptProcessing != kAbortLoadGame) {
+ _gamestate->_executionStack.clear();
+ _audio->stopAllAudio();
+ g_sci->_soundCmd->clearPlayList();
+ }
+
+ // TODO Free parser segment here
+
+ // TODO Free scripts here
+
+ // Close all opened file handles
+ _gamestate->_fileHandles.clear();
+ _gamestate->_fileHandles.resize(5);
}
// Invoked by error() when a severe error occurs
GUI::Debugger *SciEngine::getDebugger() {
if (_gamestate) {
ExecStack *xs = &(_gamestate->_executionStack.back());
- xs->addr.pc.offset = g_debugState.old_pc_offset;
- xs->sp = g_debugState.old_sp;
+ xs->addr.pc.offset = _debugState.old_pc_offset;
+ xs->sp = _debugState.old_sp;
}
- g_debugState.runningStep = 0; // Stop multiple execution
- g_debugState.seeking = kDebugSeekNothing; // Stop special seeks
+ _debugState.runningStep = 0; // Stop multiple execution
+ _debugState.seeking = kDebugSeekNothing; // Stop special seeks
return _console;
}
@@ -305,7 +496,7 @@ Console *SciEngine::getSciDebugger() {
return _console;
}
-const char* SciEngine::getGameID() const {
+const char *SciEngine::getGameIdStr() const {
return _gameDescription->gameid;
}
@@ -317,12 +508,8 @@ Common::Platform SciEngine::getPlatform() const {
return _gameDescription->platform;
}
-uint32 SciEngine::getFlags() const {
- return _gameDescription->flags;
-}
-
bool SciEngine::isDemo() const {
- return getFlags() & ADGF_DEMO;
+ return _gameDescription->flags & ADGF_DEMO;
}
Common::String SciEngine::getSavegameName(int nr) const {
@@ -334,19 +521,20 @@ Common::String SciEngine::getSavegamePattern() const {
}
Common::String SciEngine::getFilePrefix() const {
- const char* gameID = getGameID();
- if (!strcmp(gameID, "qfg2")) {
+ if (_gameId == GID_QFG2) {
// Quest for Glory 2 wants to read files from Quest for Glory 1 (EGA/VGA) to import character data
if (_gamestate->currentRoomNumber() == 805)
return "qfg1";
// TODO: Include import-room for qfg1vga
- }
- if (!strcmp(gameID, "qfg3")) {
+ } else if (_gameId == GID_QFG3) {
// Quest for Glory 3 wants to read files from Quest for Glory 2 to import character data
if (_gamestate->currentRoomNumber() == 54)
return "qfg2";
+ } else if (_gameId == GID_QFG4) {
+ // Quest for Glory 4 wants to read files from Quest for Glory 3 to import character data
+ if (_gamestate->currentRoomNumber() == 54)
+ return "qfg3";
}
- // TODO: Implement the same for qfg4, when sci32 is good enough
return _targetName;
}
@@ -362,27 +550,22 @@ Common::String SciEngine::unwrapFilename(const Common::String &name) const {
}
void SciEngine::pauseEngineIntern(bool pause) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- _gamestate->_sound.sfx_suspend(pause);
-#endif
_mixer->pauseAll(pause);
}
void SciEngine::syncSoundSettings() {
Engine::syncSoundSettings();
-#ifndef USE_OLD_MUSIC_FUNCTIONS
bool mute = false;
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
int soundVolumeMusic = (mute ? 0 : ConfMan.getInt("music_volume"));
- if (_gamestate && _gamestate->_soundCmd) {
+ if (_gamestate && g_sci->_soundCmd) {
int vol = (soundVolumeMusic + 1) * SoundCommandParser::kMaxSciVolume / Audio::Mixer::kMaxMixerVolume;
- _gamestate->_soundCmd->setMasterVolume(vol);
+ g_sci->_soundCmd->setMasterVolume(vol);
}
-#endif
}
} // End of namespace Sci
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index fdd10bcd04..72d6e7e0cb 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -28,6 +28,9 @@
#include "engines/engine.h"
#include "common/util.h"
+#include "common/random.h"
+#include "sci/engine/vm_types.h" // for Selector
+#include "sci/debug.h" // for DebugState
struct ADGameDescription;
@@ -41,9 +44,6 @@ struct ADGameDescription;
*/
namespace Sci {
-// Uncomment this to use old music functions
-//#define USE_OLD_MUSIC_FUNCTIONS
-
struct EngineState;
class Vocabulary;
class ResourceManager;
@@ -51,6 +51,8 @@ class Kernel;
class GameFeatures;
class Console;
class AudioPlayer;
+class SoundCommandParser;
+class EventManager;
class GfxAnimate;
class GfxCache;
@@ -58,14 +60,16 @@ class GfxCompare;
class GfxControls;
class GfxCoordAdjuster;
class GfxCursor;
+class GfxMacIconBar;
class GfxMenu;
class GfxPaint;
class GfxPaint16;
+class GfxPaint32;
class GfxPalette;
class GfxPorts;
class GfxScreen;
-class SciGui;
-
+class GfxText16;
+class GfxTransitions;
#ifdef ENABLE_SCI32
class SciGui32;
@@ -82,22 +86,91 @@ enum kDebugLevels {
kDebugLevelFuncCheck = 1 << 5,
kDebugLevelBresen = 1 << 6,
kDebugLevelSound = 1 << 7,
- kDebugLevelGfxDriver = 1 << 8,
- kDebugLevelBaseSetter = 1 << 9,
- kDebugLevelParser = 1 << 10,
- kDebugLevelMenu = 1 << 11,
- kDebugLevelSaid = 1 << 12,
- kDebugLevelFile = 1 << 13,
- kDebugLevelTime = 1 << 14,
- kDebugLevelRoom = 1 << 15,
- kDebugLevelAvoidPath = 1 << 16,
- kDebugLevelDclInflate = 1 << 17,
- kDebugLevelVM = 1 << 18,
- kDebugLevelScripts = 1 << 19,
- kDebugLevelGC = 1 << 20,
- kDebugLevelSci0Pic = 1 << 21,
- kDebugLevelResMan = 1 << 22,
- kDebugLevelOnStartup = 1 << 23
+ kDebugLevelBaseSetter = 1 << 8,
+ kDebugLevelParser = 1 << 9,
+ kDebugLevelSaid = 1 << 10,
+ kDebugLevelFile = 1 << 11,
+ kDebugLevelTime = 1 << 12,
+ kDebugLevelRoom = 1 << 13,
+ kDebugLevelAvoidPath = 1 << 14,
+ kDebugLevelDclInflate = 1 << 15,
+ kDebugLevelVM = 1 << 16,
+ kDebugLevelScripts = 1 << 17,
+ kDebugLevelGC = 1 << 18,
+ kDebugLevelResMan = 1 << 19,
+ kDebugLevelOnStartup = 1 << 20
+};
+
+enum SciGameId {
+ GID_ASTROCHICKEN,
+ GID_CAMELOT,
+ GID_CASTLEBRAIN,
+ GID_CHRISTMAS1988,
+ GID_CHRISTMAS1990,
+ GID_CHRISTMAS1992,
+ GID_CNICK_KQ,
+ GID_CNICK_LAURABOW,
+ GID_CNICK_LONGBOW,
+ GID_CNICK_LSL,
+ GID_CNICK_SQ,
+ GID_ECOQUEST,
+ GID_ECOQUEST2,
+ GID_FAIRYTALES,
+ GID_FREDDYPHARKAS,
+ GID_FUNSEEKER,
+ GID_GK1,
+ GID_GK2,
+ GID_HOYLE1,
+ GID_HOYLE2,
+ GID_HOYLE3,
+ GID_HOYLE4,
+ GID_ICEMAN,
+ GID_ISLANDBRAIN,
+ GID_JONES,
+ GID_KQ1,
+ GID_KQ4,
+ GID_KQ5,
+ GID_KQ6,
+ GID_KQ7,
+ GID_LAURABOW,
+ GID_LAURABOW2,
+ GID_LIGHTHOUSE,
+ GID_LONGBOW,
+ GID_LSL1,
+ GID_LSL2,
+ GID_LSL3,
+ GID_LSL5,
+ GID_LSL6,
+ GID_LSL6HIRES, // We have a separate ID for LSL6 SCI32, because it's actually a completely different game
+ GID_LSL7,
+ GID_MOTHERGOOSE,
+ GID_MOTHERGOOSEHIRES, // We have a separate ID for Mother Goose SCI32, because it's actually a completely different game
+ GID_MSASTROCHICKEN,
+ GID_PEPPER,
+ GID_PHANTASMAGORIA,
+ GID_PHANTASMAGORIA2,
+ GID_PQ1,
+ GID_PQ2,
+ GID_PQ3,
+ GID_PQ4,
+ GID_PQSWAT,
+ GID_QFG1,
+ GID_QFG1VGA,
+ GID_QFG2,
+ GID_QFG3,
+ GID_QFG4,
+ GID_RAMA,
+ GID_SHIVERS,
+ GID_SHIVERS2,
+ GID_SLATER,
+ GID_SQ1,
+ GID_SQ3,
+ GID_SQ4,
+ GID_SQ5,
+ GID_SQ6,
+ GID_TORIN,
+
+ GID_FANMADE // FIXME: Do we really need/want this?
};
/** SCI versions */
@@ -116,12 +189,6 @@ enum SciVersion {
SCI_VERSION_3 // LSL7, RAMA, Lighthouse
};
-enum MoveCountType {
- kMoveCountUninitialized,
- kIgnoreMoveCount,
- kIncrementMoveCount
-};
-
/** Supported languages */
enum kLanguage {
K_LANG_NONE = 0,
@@ -138,7 +205,7 @@ enum kLanguage {
class SciEngine : public Engine {
friend class Console;
public:
- SciEngine(OSystem *syst, const ADGameDescription *desc);
+ SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId);
~SciEngine();
// Engine APIs
@@ -153,17 +220,21 @@ public:
bool canSaveGameStateCurrently();
void syncSoundSettings();
- const char* getGameID() const;
+ const SciGameId &getGameId() const { return _gameId; }
+ const char *getGameIdStr() const;
int getResourceVersion() const;
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
- uint32 getFlags() const;
bool isDemo() const;
inline ResourceManager *getResMan() const { return _resMan; }
inline Kernel *getKernel() const { return _kernel; }
inline EngineState *getEngineState() const { return _gamestate; }
inline Vocabulary *getVocabulary() const { return _vocabulary; }
+ inline EventManager *getEventManager() const { return _eventMan; }
+ inline reg_t getGameObject() const { return _gameObj; }
+
+ Common::RandomSource &getRNG() { return _rng; }
Common::String getSavegameName(int nr) const;
Common::String getSavegamePattern() const;
@@ -176,6 +247,12 @@ public:
/** Remove the 'TARGET-' prefix of the given filename, if present. */
Common::String unwrapFilename(const Common::String &name) const;
+ void sleep(uint32 msecs);
+
+ void scriptDebug();
+ bool checkExportBreakpoint(uint16 script, uint16 pubfunct);
+ bool checkSelectorBreakpoint(reg_t send_obj, int selector);
+
public:
/**
@@ -189,9 +266,17 @@ public:
Common::String strSplit(const char *str, const char *sep = "\r----------\r");
kLanguage getSciLanguage();
+ void setSciLanguage(kLanguage lang);
+ void setSciLanguage();
Common::String getSciLanguageString(const char *str, kLanguage lang, kLanguage *lang2 = NULL) const;
+ // Check if vocabulary needs to get switched (in multilingual parser games)
+ void checkVocabularySwitch();
+
+ // Initializes ports and paint16 for non-sci32 games, also sets default palette
+ void initGraphics();
+
public:
GfxAnimate *_gfxAnimate; // Animate for 16-bit gfx
GfxCache *_gfxCache;
@@ -203,26 +288,62 @@ public:
GfxPalette *_gfxPalette;
GfxPaint *_gfxPaint;
GfxPaint16 *_gfxPaint16; // Painting in 16-bit gfx
+ GfxPaint32 *_gfxPaint32; // Painting in 32-bit gfx
GfxPorts *_gfxPorts; // Port managment for 16-bit gfx
GfxScreen *_gfxScreen;
- SciGui *_gui; /* Currently active Gui */
+ GfxText16 *_gfxText16;
+ GfxTransitions *_gfxTransitions; // transitions between screens for 16-bit gfx
+ GfxMacIconBar *_gfxMacIconBar; // Mac Icon Bar manager
#ifdef ENABLE_SCI32
- SciGui32 *_gui32; // GUI for SCI32 games
GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx
#endif
AudioPlayer *_audio;
+ SoundCommandParser *_soundCmd;
GameFeatures *_features;
+ DebugState _debugState;
+
private:
+ /**
+ * Initializes a SCI game
+ * This function must be run before script_run() is executed. Graphics data
+ * is initialized iff s->gfx_state != NULL.
+ * @param[in] s The state to operate on
+ * @return true on success, false if an error occurred.
+ */
+ bool initGame();
+
+ /**
+ * Runs a SCI game
+ * This is the main function for SCI games. It takes a valid state, loads
+ * script 0 to it, finds the game object, allocates a stack, and runs the
+ * init method of the game object. In layman's terms, this runs a SCI game.
+ * @param[in] s Pointer to the pointer of the state to operate on
+ */
+ void runGame();
+
+ /**
+ * Uninitializes an initialized SCI game
+ * This function should be run after each script_run() call.
+ * @param[in] s The state to operate on
+ */
+ void exitGame();
+
+ void initStackBaseWithSelector(Selector selector);
+
const ADGameDescription *_gameDescription;
+ const SciGameId _gameId;
ResourceManager *_resMan; /**< The resource manager */
EngineState *_gamestate;
Kernel *_kernel;
Vocabulary *_vocabulary;
+ int16 _vocabularyLanguage;
+ EventManager *_eventMan;
+ reg_t _gameObj; /**< Pointer to the game object */
Console *_console;
- OSystem *_system;
+ Common::RandomSource _rng;
};
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 331561eea4..4a2a8e65d7 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -24,21 +24,23 @@
*/
#include "sci/resource.h"
-#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
+#include "sci/engine/selector.h"
#include "sci/engine/seg_manager.h"
#include "sci/sound/audio.h"
-#include "common/system.h"
#include "common/file.h"
+#include "common/system.h"
-#include "sound/audiostream.h"
#include "sound/audiocd.h"
-#include "sound/decoders/raw.h"
-#include "sound/decoders/wave.h"
+#include "sound/audiostream.h"
+#include "sound/decoders/aiff.h"
#include "sound/decoders/flac.h"
+#include "sound/decoders/mac_snd.h"
#include "sound/decoders/mp3.h"
+#include "sound/decoders/raw.h"
#include "sound/decoders/vorbis.h"
+#include "sound/decoders/wave.h"
namespace Sci {
@@ -46,6 +48,7 @@ AudioPlayer::AudioPlayer(ResourceManager *resMan) : _resMan(resMan), _audioRate(
_syncResource(NULL), _syncOffset(0), _audioCdStart(0) {
_mixer = g_system->getMixer();
+ _wPlayFlag = false;
}
AudioPlayer::~AudioPlayer() {
@@ -64,11 +67,30 @@ int AudioPlayer::startAudio(uint16 module, uint32 number) {
Audio::AudioStream *audioStream = getAudioStream(number, module, &sampleLen);
if (audioStream) {
+ _wPlayFlag = false;
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream);
return sampleLen;
+ } else {
+ // Don't throw a warning in this case. getAudioStream() already has. Some games
+ // do miss audio entries (perhaps because of a typo, or because they were simply
+ // forgotten).
+ return 0;
}
+}
- return 0;
+int AudioPlayer::wPlayAudio(uint16 module, uint32 tuple) {
+ // Get the audio sample length and set the wPlay flag so we return 0 on
+ // position. SSCI pre-loads the audio here, but it's much easier for us to
+ // just get the sample length and return that. wPlayAudio should *not*
+ // actually start the sample.
+
+ int sampleLen = 0;
+ Audio::AudioStream *audioStream = getAudioStream(tuple, module, &sampleLen);
+ if (!audioStream)
+ warning("wPlayAudio: unable to create stream for audio tuple %d, module %d", tuple, module);
+ delete audioStream;
+ _wPlayFlag = true;
+ return sampleLen;
}
void AudioPlayer::stopAudio() {
@@ -86,6 +108,8 @@ void AudioPlayer::resumeAudio() {
int AudioPlayer::getAudioPosition() {
if (_mixer->isSoundHandleActive(_audioHandle))
return _mixer->getSoundElapsedTime(_audioHandle) * 6 / 100; // return elapsed time in ticks
+ else if (_wPlayFlag)
+ return 0; // Sound has "loaded" so return that it hasn't started
else
return -1; // Sound finished
}
@@ -161,17 +185,28 @@ static void deDPCM8(byte *soundBuf, Common::SeekableReadStream &audioStream, uin
// Sierra SOL audio file reader
// Check here for more info: http://wiki.multimedia.cx/index.php?title=Sierra_Audio
-static bool readSOLHeader(Common::SeekableReadStream *audioStream, int headerSize, uint32 &size, uint16 &audioRate, byte &audioFlags) {
- if (headerSize != 11 && headerSize != 12) {
+static bool readSOLHeader(Common::SeekableReadStream *audioStream, int headerSize, uint32 &size, uint16 &audioRate, byte &audioFlags, uint32 resSize) {
+ if (headerSize != 7 && headerSize != 11 && headerSize != 12) {
warning("SOL audio header of size %i not supported", headerSize);
return false;
}
- audioStream->readUint32LE(); // skip "SOL" + 0 (4 bytes)
+ uint32 tag = audioStream->readUint32BE();
+
+ if (tag != MKID_BE('SOL\0')) {
+ warning("No 'SOL' FourCC found");
+ return false;
+ }
+
audioRate = audioStream->readUint16LE();
audioFlags = audioStream->readByte();
- size = audioStream->readUint32LE();
+ // For the QFG3 demo format, just use the resource size
+ // Otherwise, load it from the header
+ if (headerSize == 7)
+ size = resSize;
+ else
+ size = audioStream->readUint32LE();
return true;
}
@@ -235,12 +270,15 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
uint32 audioCompressionType = audioRes->getAudioCompressionType();
if (audioCompressionType) {
+#if (defined(USE_MAD) || defined(USE_VORBIS) || defined(USE_FLAC))
// Compressed audio made by our tool
byte *compressedData = (byte *)malloc(audioRes->size);
assert(compressedData);
- // We copy over the compressed data in our own buffer. If we don't do this resourcemanager may free the data
- // later. All other compression-types already decompress completely into an additional buffer here.
- // MP3/OGG/FLAC decompression works on-the-fly instead.
+ // We copy over the compressed data in our own buffer. We have to do
+ // this, because ResourceManager may free the original data late. All
+ // other compression types already decompress completely into an
+ // additional buffer here. MP3/OGG/FLAC decompression works on-the-fly
+ // instead.
memcpy(compressedData, audioRes->data, audioRes->size);
Common::MemoryReadStream *compressedStream = new Common::MemoryReadStream(compressedData, audioRes->size, DisposeAfterUse::YES);
@@ -261,13 +299,16 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
#endif
break;
}
+#else
+ error("Compressed audio file encountered, but no appropriate decoder is compiled in");
+#endif
} else {
// Original source file
if (audioRes->_headerSize > 0) {
// SCI1.1
Common::MemoryReadStream headerStream(audioRes->_header, audioRes->_headerSize, DisposeAfterUse::NO);
- if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags)) {
+ if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags, audioRes->size)) {
Common::MemoryReadStream dataStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
data = readSOLAudio(&dataStream, size, audioFlags, flags);
}
@@ -283,22 +324,24 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
waveStream->seek(0, SEEK_SET);
audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES);
+ } else if (audioRes->size > 4 && READ_BE_UINT32(audioRes->data) == MKID_BE('FORM')) {
+ // AIFF detected
+ Common::MemoryReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+
+ // Calculate samplelen from AIFF header
+ int waveSize = 0, waveRate = 0;
+ byte waveFlags = 0;
+ Audio::loadAIFFFromStream(*waveStream, waveSize, waveRate, waveFlags);
+ *sampleLen = (waveFlags & Audio::FLAG_16BITS ? waveSize >> 1 : waveSize) * 60 / waveRate;
+
+ waveStream->seek(0, SEEK_SET);
+ audioStream = Audio::makeAIFFStream(waveStream, DisposeAfterUse::YES);
} else if (audioRes->size > 14 && READ_BE_UINT16(audioRes->data) == 1 && READ_BE_UINT16(audioRes->data + 2) == 1
&& READ_BE_UINT16(audioRes->data + 4) == 5 && READ_BE_UINT32(audioRes->data + 10) == 0x00018051) {
// Mac snd detected
- // See http://developer.apple.com/legacy/mac/library/documentation/mac/Sound/Sound-60.html#HEADING60-15 for more details
-
- uint32 soundHeaderOffset = READ_BE_UINT32(audioRes->data + 16);
- assert(READ_BE_UINT32(audioRes->data + soundHeaderOffset) == 0);
- size = READ_BE_UINT32(audioRes->data + soundHeaderOffset + 4);
- _audioRate = READ_BE_UINT16(audioRes->data + soundHeaderOffset + 8); // Really floating point, but we're just truncating
+ Common::MemoryReadStream *sndStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
- if (*(audioRes->data + soundHeaderOffset + 20) != 0)
- error("Unhandled Mac snd extended/compressed header");
-
- data = (byte *)malloc(size);
- memcpy(data, audioRes->data + soundHeaderOffset + 22, size);
- flags = Audio::FLAG_UNSIGNED;
+ audioSeekStream = Audio::makeMacSndStream(sndStream, DisposeAfterUse::YES);
} else {
// SCI1 raw audio
size = audioRes->size;
@@ -314,13 +357,12 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
}
if (audioSeekStream) {
- *sampleLen = (audioSeekStream->getLength().msecs() * 10000) / 166666; // we translate msecs to ticks
- // Original code
- //*sampleLen = (flags & Audio::FLAG_16BITS ? size >> 1 : size) * 60 / _audioRate;
+ *sampleLen = (audioSeekStream->getLength().msecs() * 60) / 1000; // we translate msecs to ticks
audioStream = audioSeekStream;
}
- // We have to make sure that we don't depend on resource manager pointers after this point, because the actual
- // audio resource may get unloaded by resource manager at any time
+ // We have to make sure that we don't depend on resource manager pointers
+ // after this point, because the actual audio resource may get unloaded by
+ // resource manager at any time.
if (audioStream)
return audioStream;
@@ -332,11 +374,11 @@ void AudioPlayer::setSoundSync(ResourceId id, reg_t syncObjAddr, SegManager *seg
_syncOffset = 0;
if (_syncResource) {
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), 0);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), 0);
} else {
warning("setSoundSync: failed to find resource %s", id.toString().c_str());
// Notify the scripts to stop sound sync
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET);
}
}
@@ -352,8 +394,8 @@ void AudioPlayer::doSoundSync(reg_t syncObjAddr, SegManager *segMan) {
_syncOffset += 2;
}
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncTime), syncTime);
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), syncCue);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncTime), syncTime);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), syncCue);
}
}
diff --git a/engines/sci/sound/audio.h b/engines/sci/sound/audio.h
index 9fc3cbac51..7c1221fc4c 100644
--- a/engines/sci/sound/audio.h
+++ b/engines/sci/sound/audio.h
@@ -33,8 +33,7 @@
namespace Sci {
enum AudioCommands {
- // TODO: find the difference between kSci1AudioWPlay and kSci1AudioPlay
- kSciAudioWPlay = 1, /* Plays an audio stream */
+ kSciAudioWPlay = 1, /* Loads an audio stream */
kSciAudioPlay = 2, /* Plays an audio stream */
kSciAudioStop = 3, /* Stops an audio stream */
kSciAudioPause = 4, /* Pauses an audio stream */
@@ -69,6 +68,7 @@ public:
Audio::RewindableAudioStream *getAudioStream(uint32 number, uint32 volume, int *sampleLen);
int getAudioPosition();
int startAudio(uint16 module, uint32 tuple);
+ int wPlayAudio(uint16 module, uint32 tuple);
void stopAudio();
void pauseAudio();
void resumeAudio();
@@ -92,6 +92,7 @@ private:
Resource *_syncResource; /**< Used by kDoSync for speech syncing in CD talkie games */
uint _syncOffset;
uint32 _audioCdStart;
+ bool _wPlayFlag;
};
} // End of namespace Sci
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index a743e4b5d9..55c3640c9d 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -704,6 +704,7 @@ void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLe
void MidiDriver_AdLib::setPatch(int voice, int patch) {
if ((patch < 0) || ((uint)patch >= _patches.size())) {
warning("ADLIB: Invalid patch %i requested", patch);
+ // Substitute instrument 0
patch = 0;
}
@@ -749,17 +750,24 @@ void MidiDriver_AdLib::playSwitch(bool play) {
bool MidiDriver_AdLib::loadResource(const byte *data, uint size) {
if ((size != 1344) && (size != 2690) && (size != 5382)) {
- warning("ADLIB: Unsupported patch format (%i bytes)", size);
+ error("ADLIB: Unsupported patch format (%i bytes)", size);
return false;
}
for (int i = 0; i < 48; i++)
loadInstrument(data + (28 * i));
- if (size == 2690) {
+ if (size == 1344) {
+ byte dummy[28] = {0};
+
+ // Only 48 instruments, add dummies
+ for (int i = 0; i < 48; i++)
+ loadInstrument(dummy);
+ } else if (size == 2690) {
for (int i = 48; i < 96; i++)
loadInstrument(data + 2 + (28 * i));
- } else if (size == 5382) {
+ } else {
+ // SCI1.1 and later
for (int i = 48; i < 190; i++)
loadInstrument(data + (28 * i));
_rhythmKeyMap = new byte[kRhythmKeys];
diff --git a/engines/sci/sound/drivers/amiga.cpp b/engines/sci/sound/drivers/amigamac.cpp
index 0413dbb79e..4fb9146b53 100644
--- a/engines/sci/sound/drivers/amiga.cpp
+++ b/engines/sci/sound/drivers/amigamac.cpp
@@ -25,6 +25,7 @@
#include "sound/softsynth/emumidi.h"
#include "sci/sound/drivers/mididriver.h"
+#include "sci/resource.h"
#include "common/file.h"
#include "common/frac.h"
@@ -34,35 +35,14 @@ namespace Sci {
/* #define DEBUG */
-// Frequencies for every note
-// FIXME Store only one octave
-static const int freq_table[] = {
- 58, 62, 65, 69, 73, 78, 82, 87,
- 92, 98, 104, 110, 117, 124, 131, 139,
- 147, 156, 165, 175, 185, 196, 208, 220,
- 234, 248, 262, 278, 294, 312, 331, 350,
- 371, 393, 417, 441, 468, 496, 525, 556,
- 589, 625, 662, 701, 743, 787, 834, 883,
- 936, 992, 1051, 1113, 1179, 1250, 1324, 1403,
- 1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227,
- 2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535,
- 3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612,
- 5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908,
- 9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142,
- 14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449,
- 23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635,
- 37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568,
- 59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796
-};
-
-class MidiDriver_Amiga : public MidiDriver_Emulated {
+class MidiDriver_AmigaMac : public MidiDriver_Emulated {
public:
enum {
kVoices = 4
};
- MidiDriver_Amiga(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { }
- virtual ~MidiDriver_Amiga() { }
+ MidiDriver_AmigaMac(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { }
+ virtual ~MidiDriver_AmigaMac() { }
// MidiDriver
int open();
@@ -99,6 +79,7 @@ private:
int instrument;
int volume;
int pan;
+ uint16 pitch;
};
struct Envelope {
@@ -121,7 +102,7 @@ private:
frac_t rate;
};
- struct Instrument {
+ struct InstrumentSample {
char name[30];
int mode;
int size; // Size of non-looping part in bytes
@@ -130,35 +111,55 @@ private:
Envelope envelope[4]; // Envelope
int8 *samples;
int8 *loop;
+ int16 startNote;
+ int16 endNote;
+ bool isUnsigned;
+ uint16 baseFreq;
+ uint16 baseNote;
+ int16 fixedNote;
+ };
+
+ class Instrument : public Common::Array<InstrumentSample *> {
+ public:
+ char name[30];
};
struct Bank {
char name[30];
uint size;
- Instrument *instruments[256];
+ Common::Array<Instrument> instruments;
};
+ bool _isSci1;
bool _playSwitch;
int _masterVolume;
int _frequency;
Envelope _envDecay;
Bank _bank; // Instrument bank
+ double _freqTable[48];
Channel _channels[MIDI_CHANNELS];
/* Internal channels */
Voice _voices[kChannels];
void setEnvelope(Voice *channel, Envelope *envelope, int phase);
- int interpolate(int8 *samples, frac_t offset);
+ void setOutputFrac(int voice);
+ int interpolate(int8 *samples, frac_t offset, bool isUnsigned);
void playInstrument(int16 *dest, Voice *channel, int count);
void changeInstrument(int channel, int instrument);
void stopChannel(int ch);
void stopNote(int ch, int note);
void startNote(int ch, int note, int velocity);
- Instrument *readInstrument(Common::File &file, int *id);
+ InstrumentSample *findInstrument(int instrument, int note);
+ void pitchWheel(int ch, uint16 pitch);
+
+ bool loadInstrumentsSCI0(Common::File &file);
+ bool loadInstrumentsSCI0Mac(Common::SeekableReadStream &file);
+ InstrumentSample *readInstrumentSCI0(Common::SeekableReadStream &file, int *id);
+ bool loadInstrumentsSCI1(Common::SeekableReadStream &file);
};
-void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase) {
+void MidiDriver_AmigaMac::setEnvelope(Voice *channel, Envelope *envelope, int phase) {
channel->envelope = phase;
channel->envelope_samples = envelope[phase].length;
@@ -168,25 +169,32 @@ void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase
channel->velocity = envelope[phase - 1].target;
}
-int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset) {
+int MidiDriver_AmigaMac::interpolate(int8 *samples, frac_t offset, bool isUnsigned) {
int x = fracToInt(offset);
- int diff = (samples[x + 1] - samples[x]) << 8;
+ if (isUnsigned) {
+ int s1 = (byte)samples[x] - 0x80;
+ int s2 = (byte)samples[x + 1] - 0x80;
+ int diff = (s2 - s1) << 8;
+ return (s1 << 8) + fracToInt(diff * (offset & FRAC_LO_MASK));
+ }
+
+ int diff = (samples[x + 1] - samples[x]) << 8;
return (samples[x] << 8) + fracToInt(diff * (offset & FRAC_LO_MASK));
}
-void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
+void MidiDriver_AmigaMac::playInstrument(int16 *dest, Voice *channel, int count) {
int index = 0;
int vol = _channels[channel->hw_channel].volume;
- Instrument *instrument = _bank.instruments[channel->instrument];
+ InstrumentSample *instrument = findInstrument(channel->instrument, channel->note);
while (1) {
/* Available source samples until end of segment */
frac_t lin_avail;
- int seg_end, rem, i, amount;
+ uint32 seg_end, rem, i, amount;
int8 *samples;
- if (channel->looping) {
+ if (channel->looping && instrument->loop) {
samples = instrument->loop;
seg_end = instrument->loop_size;
} else {
@@ -208,11 +216,11 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
amount = rem;
/* Stop at next envelope event */
- if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples))
+ if ((channel->envelope_samples != -1) && (amount > (uint32)channel->envelope_samples))
amount = channel->envelope_samples;
for (i = 0; i < amount; i++) {
- dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
+ dest[index++] = interpolate(samples, channel->offset, instrument->isUnsigned) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
channel->offset += channel->rate;
}
@@ -263,7 +271,7 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
if (index == count)
break;
- if (fracToInt(channel->offset) >= seg_end) {
+ if ((uint32)fracToInt(channel->offset) >= seg_end) {
if (instrument->mode & kModeLoop) {
/* Loop the samples */
channel->offset -= intToFrac(seg_end);
@@ -277,9 +285,9 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
}
}
-void MidiDriver_Amiga::changeInstrument(int channel, int instrument) {
+void MidiDriver_AmigaMac::changeInstrument(int channel, int instrument) {
#ifdef DEBUG
- if (_bank.instruments[instrument])
+ if (_bank.instruments[instrument][0])
printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument);
else
warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel);
@@ -287,7 +295,7 @@ void MidiDriver_Amiga::changeInstrument(int channel, int instrument) {
_channels[channel].instrument = instrument;
}
-void MidiDriver_Amiga::stopChannel(int ch) {
+void MidiDriver_AmigaMac::stopChannel(int ch) {
int i;
/* Start decay phase for note on this hw channel, if any */
@@ -300,9 +308,16 @@ void MidiDriver_Amiga::stopChannel(int ch) {
}
}
-void MidiDriver_Amiga::stopNote(int ch, int note) {
+void MidiDriver_AmigaMac::pitchWheel(int ch, uint16 pitch) {
+ _channels[ch].pitch = pitch;
+
+ for (int i = 0; i < kChannels; i++)
+ if (_voices[i].note != -1 && _voices[i].hw_channel == ch)
+ setOutputFrac(i);
+}
+
+void MidiDriver_AmigaMac::stopNote(int ch, int note) {
int channel;
- Instrument *instrument;
for (channel = 0; channel < kChannels; channel++)
if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay)
@@ -315,15 +330,75 @@ void MidiDriver_Amiga::stopNote(int ch, int note) {
return;
}
- instrument = _bank.instruments[_voices[channel].instrument];
+ InstrumentSample *instrument = findInstrument(_voices[channel].instrument, note);
+
+ // FIXME: SCI1 envelope support is not perfect yet
/* Start the envelope phases for note-off if looping is on and envelope is enabled */
if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
setEnvelope(&_voices[channel], instrument->envelope, 2);
}
-void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
- Instrument *instrument;
+MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::findInstrument(int instrument, int note) {
+ if ((uint)instrument >= _bank.instruments.size())
+ return 0;
+
+ for (uint32 i = 0; i < _bank.instruments[instrument].size(); i++) {
+ InstrumentSample *sample = _bank.instruments[instrument][i];
+ if (note >= sample->startNote && note <= sample->endNote)
+ return sample;
+ }
+
+ return 0;
+}
+
+void MidiDriver_AmigaMac::setOutputFrac(int voice) {
+ InstrumentSample *instrument = findInstrument(_voices[voice].instrument, _voices[voice].note);
+
+ int fnote = 0;
+
+ if (instrument->fixedNote == -1) {
+ fnote = _voices[voice].note;
+
+ // Handle SCI0-style transposing here
+ if (!_isSci1)
+ fnote += instrument->transpose;
+
+ if (fnote < 0 || fnote > 127) {
+ warning("[sfx:seq:amiga] illegal note %i", fnote);
+ return;
+ }
+ } else
+ fnote = instrument->fixedNote;
+
+ // Compute rate for note
+ int mulFact = 1, divFact = 1;
+
+ fnote -= instrument->baseNote;
+ fnote *= 4;
+ // FIXME: check how SSCI maps this
+ fnote += (_channels[_voices[voice].hw_channel].pitch - 0x2000) / 169;
+
+ while (fnote < 0) {
+ divFact *= 2;
+ fnote += 12 * 4;
+ }
+
+ while (fnote >= 12 * 4) {
+ mulFact *= 2;
+ fnote -= 12 * 4;
+ }
+
+ double freq = _freqTable[fnote] * instrument->baseFreq * mulFact / divFact;
+
+ // Handle SCI1-style transposing here
+ if (instrument->transpose && _isSci1)
+ freq = freq + ((_freqTable[4] - 1.0) * freq * (double)instrument->transpose / (double)16);
+
+ _voices[voice].rate = doubleToFrac(freq / _frequency);
+}
+
+void MidiDriver_AmigaMac::startNote(int ch, int note, int velocity) {
int channel;
if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) {
@@ -331,7 +406,7 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
return;
}
- instrument = _bank.instruments[_channels[ch].instrument];
+ InstrumentSample *instrument = findInstrument(_channels[ch].instrument, note);
if (!instrument) {
warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument);
@@ -349,19 +424,6 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
stopChannel(ch);
- if (instrument->mode & kModePitch) {
- int fnote = note + instrument->transpose;
-
- if (fnote < 0 || fnote > 127) {
- warning("[sfx:seq:amiga] illegal note %i\n", fnote);
- return;
- }
-
- /* Compute rate for note */
- _voices[channel].rate = doubleToFrac(freq_table[fnote] / (double) _frequency);
- } else
- _voices[channel].rate = doubleToFrac(kBaseFreq / (double) _frequency);
-
_voices[channel].instrument = _channels[ch].instrument;
_voices[channel].note = note;
_voices[channel].note_velocity = velocity;
@@ -377,30 +439,34 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
_voices[channel].hw_channel = ch;
_voices[channel].decay = 0;
_voices[channel].looping = 0;
+ setOutputFrac(channel);
}
-MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &file, int *id) {
- Instrument *instrument;
+MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(Common::SeekableReadStream &file, int *id) {
byte header[61];
- int size;
- int seg_size[3];
- int loop_offset;
- int i;
if (file.read(header, 61) < 61) {
warning("[sfx:seq:amiga] failed to read instrument header");
return NULL;
}
- instrument = new Instrument;
-
+ int seg_size[3];
seg_size[0] = READ_BE_UINT16(header + 35) * 2;
seg_size[1] = READ_BE_UINT16(header + 41) * 2;
seg_size[2] = READ_BE_UINT16(header + 47) * 2;
+ InstrumentSample *instrument = new InstrumentSample;
+
+ instrument->startNote = 0;
+ instrument->endNote = 127;
+ instrument->isUnsigned = false;
+ instrument->baseFreq = kBaseFreq;
+ instrument->baseNote = 101;
+ instrument->fixedNote = 101;
+
instrument->mode = header[33];
instrument->transpose = (int8) header[34];
- for (i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
int length = (int8) header[49 + i];
if (length == 0 && i > 0)
@@ -413,13 +479,14 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
/* Final target must be 0 */
instrument->envelope[3].target = 0;
- loop_offset = READ_BE_UINT32(header + 37) & ~1;
- size = seg_size[0] + seg_size[1] + seg_size[2];
+ int loop_offset = READ_BE_UINT32(header + 37) & ~1;
+ int size = seg_size[0] + seg_size[1] + seg_size[2];
*id = READ_BE_UINT16(header);
strncpy(instrument->name, (char *) header + 2, 29);
instrument->name[29] = 0;
+
#ifdef DEBUG
printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
*id, instrument->name, size);
@@ -429,6 +496,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
printf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
printf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
#endif
+
instrument->samples = (int8 *) malloc(size + 1);
if (file.read(instrument->samples, size) < (unsigned int)size) {
warning("[sfx:seq:amiga] failed to read instrument samples");
@@ -437,6 +505,9 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
return NULL;
}
+ if (instrument->mode & kModePitch)
+ instrument->fixedNote = -1;
+
if (instrument->mode & kModeLoop) {
if (loop_offset + seg_size[1] > size) {
#ifdef DEBUG
@@ -463,6 +534,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
instrument->loop[instrument->loop_size] = instrument->loop[0];
} else {
instrument->loop = NULL;
+ instrument->loop_size = 0;
instrument->size = size;
instrument->samples[instrument->size] = 0;
}
@@ -470,7 +542,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
return instrument;
}
-uint32 MidiDriver_Amiga::property(int prop, uint32 param) {
+uint32 MidiDriver_AmigaMac::property(int prop, uint32 param) {
switch(prop) {
case MIDI_PROP_MASTER_VOLUME:
if (param != 0xffff)
@@ -482,28 +554,17 @@ uint32 MidiDriver_Amiga::property(int prop, uint32 param) {
return 0;
}
-int MidiDriver_Amiga::open() {
+int MidiDriver_AmigaMac::open() {
+ _isSci1 = false;
+
+ for (int i = 0; i < 48; i++)
+ _freqTable[i] = pow(2, i / (double)48);
+
_frequency = _mixer->getOutputRate();
_envDecay.length = _frequency / (32 * 64);
_envDecay.delta = 1;
_envDecay.target = 0;
- Common::File file;
- byte header[40];
-
- if (!file.open("bank.001")) {
- warning("[sfx:seq:amiga] file bank.001 not found");
- return Common::kUnknownError;
- }
-
- if (file.read(header, 40) < 40) {
- warning("[sfx:seq:amiga] failed to read header of file bank.001");
- return Common::kUnknownError;
- }
-
- for (uint i = 0; i < 256; i++)
- _bank.instruments[i] = NULL;
-
for (uint i = 0; i < kChannels; i++) {
_voices[i].note = -1;
_voices[i].hw_channel = 0;
@@ -513,32 +574,46 @@ int MidiDriver_Amiga::open() {
_channels[i].instrument = -1;
_channels[i].volume = 127;
_channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? kPanLeft : kPanRight);
+ _channels[i].pitch = 0x2000;
}
- _bank.size = READ_BE_UINT16(header + 38);
- strncpy(_bank.name, (char *) header + 8, 29);
- _bank.name[29] = 0;
-#ifdef DEBUG
- printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
-#endif
-
- for (uint i = 0; i < _bank.size; i++) {
- int id;
- Instrument *instrument = readInstrument(file, &id);
+ Common::File file;
- if (!instrument) {
- warning("[sfx:seq:amiga] failed to read bank.001");
+ if (file.open("bank.001")) {
+ if (!loadInstrumentsSCI0(file)) {
+ file.close();
return Common::kUnknownError;
}
+ file.close();
+ } else {
+ ResourceManager *resMan = g_sci->getResMan();
- if (id < 0 || id > 255) {
- warning("[sfx:seq:amiga] Error: instrument ID out of bounds");
+ Resource *resource = resMan->findResource(ResourceId(kResourceTypePatch, 7), false);
+ if (!resource)
+ resource = resMan->findResource(ResourceId(kResourceTypePatch, 9), false);
+
+ // If we have a patch by this point, it's SCI1
+ if (resource)
+ _isSci1 = true;
+
+ // Check for the SCI0 Mac patch
+ if (!resource)
+ resource = resMan->findResource(ResourceId(kResourceTypePatch, 200), false);
+
+ if (!resource) {
+ warning("Could not open patch for Amiga sound driver");
return Common::kUnknownError;
}
- _bank.instruments[id] = instrument;
- }
+ Common::MemoryReadStream stream(resource->data, resource->size);
+ if (_isSci1) {
+ if (!loadInstrumentsSCI1(stream))
+ return Common::kUnknownError;
+ } else if (!loadInstrumentsSCI0Mac(stream))
+ return Common::kUnknownError;
+ }
+
MidiDriver_Emulated::open();
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
@@ -546,28 +621,30 @@ int MidiDriver_Amiga::open() {
return Common::kNoError;
}
-void MidiDriver_Amiga::close() {
+void MidiDriver_AmigaMac::close() {
_mixer->stopHandle(_mixerSoundHandle);
for (uint i = 0; i < _bank.size; i++) {
- if (_bank.instruments[i]) {
- if (_bank.instruments[i]->loop)
- free(_bank.instruments[i]->loop);
- free(_bank.instruments[i]->samples);
- delete _bank.instruments[i];
+ for (uint32 j = 0; j < _bank.instruments[i].size(); j++) {
+ if (_bank.instruments[i][j]) {
+ if (_bank.instruments[i][j]->loop)
+ free(_bank.instruments[i][j]->loop);
+ free(_bank.instruments[i][j]->samples);
+ delete _bank.instruments[i][j];
+ }
}
}
}
-void MidiDriver_Amiga::playSwitch(bool play) {
+void MidiDriver_AmigaMac::playSwitch(bool play) {
_playSwitch = play;
}
-void MidiDriver_Amiga::setVolume(byte volume_) {
+void MidiDriver_AmigaMac::setVolume(byte volume_) {
_masterVolume = volume_;
}
-void MidiDriver_Amiga::send(uint32 b) {
+void MidiDriver_AmigaMac::send(uint32 b) {
byte command = b & 0xf0;
byte channel = b & 0xf;
byte op1 = (b >> 8) & 0xff;
@@ -603,12 +680,15 @@ void MidiDriver_Amiga::send(uint32 b) {
case 0xc0:
changeInstrument(channel, op1);
break;
+ case 0xe0:
+ pitchWheel(channel, (op2 << 7) | op1);
+ break;
default:
warning("[sfx:seq:amiga] unknown event %02x", command);
}
}
-void MidiDriver_Amiga::generateSamples(int16 *data, int len) {
+void MidiDriver_AmigaMac::generateSamples(int16 *data, int len) {
if (len == 0)
return;
@@ -651,24 +731,256 @@ void MidiDriver_Amiga::generateSamples(int16 *data, int len) {
free(buffers);
}
-class MidiPlayer_Amiga : public MidiPlayer {
+bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) {
+ _isSci1 = false;
+
+ byte header[40];
+
+ if (file.read(header, 40) < 40) {
+ warning("[sfx:seq:amiga] failed to read header of file bank.001");
+ return false;
+ }
+
+ _bank.size = READ_BE_UINT16(header + 38);
+ strncpy(_bank.name, (char *) header + 8, 29);
+ _bank.name[29] = 0;
+#ifdef DEBUG
+ printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
+#endif
+
+ for (uint i = 0; i < _bank.size; i++) {
+ int id;
+ InstrumentSample *instrument = readInstrumentSCI0(file, &id);
+
+ if (!instrument) {
+ warning("[sfx:seq:amiga] failed to read bank.001");
+ return false;
+ }
+
+ if (id < 0 || id > 255) {
+ warning("[sfx:seq:amiga] Error: instrument ID out of bounds");
+ return false;
+ }
+
+ if ((uint)id >= _bank.instruments.size())
+ _bank.instruments.resize(id + 1);
+
+ _bank.instruments[id].push_back(instrument);
+ memcpy(_bank.instruments[id].name, instrument->name, sizeof(instrument->name));
+ }
+
+ return true;
+}
+
+bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &file) {
+ byte header[40];
+
+ if (file.read(header, 40) < 40) {
+ warning("[sfx:seq:amiga] failed to read header of file patch.200");
+ return false;
+ }
+
+ _bank.size = 128;
+ strncpy(_bank.name, (char *) header + 8, 29);
+ _bank.name[29] = 0;
+#ifdef DEBUG
+ printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
+#endif
+
+ Common::Array<uint32> instrumentOffsets;
+ instrumentOffsets.resize(_bank.size);
+ _bank.instruments.resize(_bank.size);
+
+ for (uint32 i = 0; i < _bank.size; i++)
+ instrumentOffsets[i] = file.readUint32BE();
+
+ for (uint i = 0; i < _bank.size; i++) {
+ // 0 signifies it doesn't exist
+ if (instrumentOffsets[i] == 0)
+ continue;
+
+ file.seek(instrumentOffsets[i]);
+
+ uint16 id = file.readUint16BE();
+ if (id != i)
+ error("Instrument number mismatch");
+
+ InstrumentSample *instrument = new InstrumentSample;
+
+ instrument->startNote = 0;
+ instrument->endNote = 127;
+ instrument->isUnsigned = true;
+ instrument->baseFreq = kBaseFreq;
+ instrument->baseNote = 101;
+ instrument->fixedNote = 101;
+ instrument->mode = file.readUint16BE();
+
+ // Read in the offsets
+ int32 seg_size[3];
+ seg_size[0] = file.readUint32BE();
+ seg_size[1] = file.readUint32BE();
+ seg_size[2] = file.readUint32BE();
+
+ instrument->transpose = file.readUint16BE();
+
+ for (byte j = 0; j < 4; j++) {
+ int length = (int8)file.readByte();
+
+ if (length == 0 && j > 0)
+ length = 256;
+
+ instrument->envelope[j].length = length * _frequency / 60;
+ instrument->envelope[j].delta = (int8)file.readByte();
+ instrument->envelope[j].target = file.readByte();
+ }
+
+ // Final target must be 0
+ instrument->envelope[3].target = 0;
+
+ file.read(instrument->name, 30);
+
+ if (instrument->mode & kModePitch)
+ instrument->fixedNote = -1;
+
+ uint32 size = seg_size[2];
+ uint32 loop_offset = seg_size[0];
+
+ instrument->samples = (int8 *)malloc(size + 1);
+ if (file.read(instrument->samples, size) < size) {
+ warning("[sfx:seq:amiga] failed to read instrument sample");
+ free(instrument->samples);
+ delete instrument;
+ continue;
+ }
+
+ if (instrument->mode & kModeLoop) {
+ instrument->size = seg_size[0];
+ instrument->loop_size = seg_size[1] - seg_size[0];
+
+ instrument->loop = (int8*)malloc(instrument->loop_size + 1);
+ memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size);
+
+ instrument->samples[instrument->size] = instrument->loop[0];
+ instrument->loop[instrument->loop_size] = instrument->loop[0];
+ } else {
+ instrument->loop = NULL;
+ instrument->loop_size = 0;
+ instrument->size = size;
+ instrument->samples[instrument->size] = (int8)0x80;
+ }
+
+ _bank.instruments[id].push_back(instrument);
+ memcpy(_bank.instruments[id].name, instrument->name, sizeof(instrument->name));
+ }
+
+ return true;
+}
+
+bool MidiDriver_AmigaMac::loadInstrumentsSCI1(Common::SeekableReadStream &file) {
+ _bank.size = 128;
+
+ Common::Array<uint32> instrumentOffsets;
+ instrumentOffsets.resize(_bank.size);
+ _bank.instruments.resize(_bank.size);
+
+ for (uint32 i = 0; i < _bank.size; i++)
+ instrumentOffsets[i] = file.readUint32BE();
+
+ for (uint32 i = 0; i < _bank.size; i++) {
+ // 0 signifies it doesn't exist
+ if (instrumentOffsets[i] == 0)
+ continue;
+
+ file.seek(instrumentOffsets[i]);
+
+ // Read in the instrument name
+ file.read(_bank.instruments[i].name, 10); // last two bytes are always 0
+
+ for (uint32 j = 0; ; j++) {
+ InstrumentSample *sample = new InstrumentSample;
+ memset(sample, 0, sizeof(InstrumentSample));
+
+ sample->startNote = file.readSint16BE();
+
+ // startNote being -1 signifies we're done with this instrument
+ if (sample->startNote == -1) {
+ delete sample;
+ break;
+ }
+
+ sample->endNote = file.readSint16BE();
+ uint32 samplePtr = file.readUint32BE();
+ sample->transpose = file.readSint16BE();
+ for (int env = 0; env < 3; env++) {
+ sample->envelope[env].length = file.readByte() * _frequency / 60;
+ sample->envelope[env].delta = (env == 0 ? 10 : -10);
+ sample->envelope[env].target = file.readByte();
+ }
+
+ sample->envelope[3].length = 0;
+ sample->fixedNote = file.readSint16BE();
+ int16 loop = file.readSint16BE();
+ uint32 nextSamplePos = file.pos();
+
+ file.seek(samplePtr);
+ file.read(sample->name, 8);
+
+ sample->isUnsigned = file.readUint16BE() == 0;
+ uint16 phase1Offset = file.readUint16BE();
+ uint16 phase1End = file.readUint16BE();
+ uint16 phase2Offset = file.readUint16BE();
+ uint16 phase2End = file.readUint16BE();
+ sample->baseNote = file.readUint16BE();
+ uint32 periodTableOffset = file.readUint32BE();
+ uint32 sampleDataPos = file.pos();
+
+ sample->size = phase1End - phase1Offset + 1;
+ sample->loop_size = phase2End - phase2Offset + 1;
+
+ sample->samples = (int8 *)malloc(sample->size + 1);
+ file.seek(phase1Offset + sampleDataPos);
+ file.read(sample->samples, sample->size);
+ sample->samples[sample->size] = (sample->isUnsigned ? (int8)0x80 : 0);
+
+ if (loop == 0 && sample->loop_size > 1) {
+ sample->loop = (int8 *)malloc(sample->loop_size + 1);
+ file.seek(phase2Offset + sampleDataPos);
+ file.read(sample->loop, sample->loop_size);
+ sample->mode |= kModeLoop;
+ sample->samples[sample->size] = sample->loop[0];
+ sample->loop[sample->loop_size] = sample->loop[0];
+ }
+
+ _bank.instruments[i].push_back(sample);
+
+ file.seek(periodTableOffset + 0xe0);
+ sample->baseFreq = file.readUint16BE();
+
+ file.seek(nextSamplePos);
+ }
+ }
+
+ return true;
+}
+
+class MidiPlayer_AmigaMac : public MidiPlayer {
public:
- MidiPlayer_Amiga(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_Amiga(g_system->getMixer()); }
+ MidiPlayer_AmigaMac(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_AmigaMac(g_system->getMixer()); }
byte getPlayId();
- int getPolyphony() const { return MidiDriver_Amiga::kVoices; }
+ int getPolyphony() const { return MidiDriver_AmigaMac::kVoices; }
bool hasRhythmChannel() const { return false; }
- void setVolume(byte volume) { static_cast<MidiDriver_Amiga *>(_driver)->setVolume(volume); }
- void playSwitch(bool play) { static_cast<MidiDriver_Amiga *>(_driver)->playSwitch(play); }
+ void setVolume(byte volume) { static_cast<MidiDriver_AmigaMac *>(_driver)->setVolume(volume); }
+ void playSwitch(bool play) { static_cast<MidiDriver_AmigaMac *>(_driver)->playSwitch(play); }
void loadInstrument(int idx, byte *data);
};
-MidiPlayer *MidiPlayer_Amiga_create(SciVersion version) {
- return new MidiPlayer_Amiga(version);
+MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version) {
+ return new MidiPlayer_AmigaMac(version);
}
-byte MidiPlayer_Amiga::getPlayId() {
- if (_version != SCI_VERSION_0_LATE)
- error("Amiga sound support not available for this SCI version");
+byte MidiPlayer_AmigaMac::getPlayId() {
+ if (_version > SCI_VERSION_0_LATE)
+ return 0x06;
return 0x40;
}
diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp
index 7e9fbd51a1..ab9b2e3df5 100644
--- a/engines/sci/sound/drivers/fb01.cpp
+++ b/engines/sci/sound/drivers/fb01.cpp
@@ -128,8 +128,8 @@ private:
};
MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL) {
- MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
- _driver = createMidi(midiType);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI);
+ _driver = createMidi(dev);
_sysExBuf[0] = 0x43;
_sysExBuf[1] = 0x75;
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 2432a8fab0..1ef0781906 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -55,6 +55,7 @@ public:
bool hasRhythmChannel() const { return true; }
byte getPlayId();
int getPolyphony() const { return kVoices; }
+ int getFirstChannel();
void setVolume(byte volume);
int getVolume();
void setReverb(byte reverb);
@@ -119,10 +120,10 @@ private:
};
MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) {
- MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
- _driver = createMidi(midiType);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI);
+ _driver = createMidi(dev);
- if (midiType == MD_MT32 || ConfMan.getBool("native_mt32"))
+ if (MidiDriver::getMusicType(dev) == MT_MT32 || ConfMan.getBool("native_mt32"))
_isMt32 = true;
_sysExBuf[0] = 0x41;
@@ -271,6 +272,17 @@ void MidiPlayer_Midi::setPatch(int channel, int patch) {
_driver->setPitchBendRange(channel, bendRange);
_driver->send(0xc0 | channel, _patchMap[patch], 0);
+
+ // Send a pointless command to work around a firmware bug in common
+ // USB-MIDI cables. If the first MIDI command in a USB packet is a
+ // Cx or Dx command, the second command in the packet is dropped
+ // somewhere.
+ // FIXME: consider putting a workaround in the MIDI backend drivers
+ // instead.
+ // Known to be affected: alsa, coremidi
+ // Known *not* to be affected: windows (only seems to send one MIDI
+ // command per USB packet even if the device allows larger packets).
+ _driver->send(0xb0 | channel, 0x0a, _channels[channel].pan);
}
void MidiPlayer_Midi::send(uint32 b) {
@@ -306,6 +318,13 @@ void MidiPlayer_Midi::send(uint32 b) {
}
}
+// We return 1 for mt32, because if we remap channels to 0 for mt32, those won't get played at all
+int MidiPlayer_Midi::getFirstChannel() {
+ if (_isMt32)
+ return 1;
+ return 0;
+}
+
void MidiPlayer_Midi::setVolume(byte volume) {
_masterVolume = volume;
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index 12d3e57f5d..2db6f25c70 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -86,6 +86,7 @@ public:
virtual byte getPlayId() = 0;
virtual int getPolyphony() const = 0;
+ virtual int getFirstChannel() { return 0; }
virtual void setVolume(byte volume) {
if(_driver)
@@ -112,7 +113,7 @@ protected:
};
extern MidiPlayer *MidiPlayer_AdLib_create(SciVersion version);
-extern MidiPlayer *MidiPlayer_Amiga_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version);
extern MidiPlayer *MidiPlayer_PCJr_create(SciVersion version);
extern MidiPlayer *MidiPlayer_PCSpeaker_create(SciVersion version);
extern MidiPlayer *MidiPlayer_Midi_create(SciVersion version);
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index 2068ea9a33..6ec28a8b02 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -43,27 +43,39 @@ enum SciMidiCommands {
// MidiParser_SCI
//
-MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) :
+MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion, SciMusic *music) :
MidiParser() {
_soundVersion = soundVersion;
+ _music = music;
_mixedData = NULL;
// mididata contains delta in 1/60th second
// values of ppqn and tempo are found experimentally and may be wrong
_ppqn = 1;
setTempo(16667);
- _volume = 0;
+ _volume = 127;
_signalSet = false;
_signalToSet = 0;
_dataincAdd = false;
_dataincToAdd = 0;
_resetOnPause = false;
- _channelsUsed = 0;
+ _pSnd = 0;
}
MidiParser_SCI::~MidiParser_SCI() {
unloadMusic();
+ // we do this, so that MidiParser won't be able to call his own ::allNotesOff()
+ // this one would affect all channels and we can't let that happen
+ _driver = 0;
+}
+
+void MidiParser_SCI::mainThreadBegin() {
+ _mainThreadCalled = true;
+}
+
+void MidiParser_SCI::mainThreadEnd() {
+ _mainThreadCalled = false;
}
bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion) {
@@ -72,7 +84,14 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
_pSnd = psnd;
_soundVersion = soundVersion;
- setVolume(psnd->volume);
+ for (int i = 0; i < 16; i++) {
+ _channelUsed[i] = false;
+ _channelRemap[i] = -1;
+ _channelMuted[i] = false;
+ _channelVolume[i] = 127;
+ }
+ _channelRemap[9] = 9; // never map channel 9, because that's used for percussion
+ _channelRemap[15] = 15; // never map channel 15, because thats used by sierra internally
if (channelFilterMask) {
// SCI0 only has 1 data stream, but we need to filter out channels depending on music hardware selection
@@ -83,31 +102,272 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
_num_tracks = 1;
_tracks[0] = _mixedData;
- setTrack(0);
+ if (_pSnd)
+ setTrack(0);
_loopTick = 0;
- _channelsUsed = 0;
-
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- // Set initial voice count
- for (int i = 0; i < 16; ++i) {
- byte voiceCount = 0;
- if (channelFilterMask & (1 << i))
- voiceCount = psnd->soundRes->getInitialVoiceCount(i);
- _driver->send(0xB0 | i, 0x4B, voiceCount);
+
+ return true;
+}
+
+byte MidiParser_SCI::midiGetNextChannel(long ticker) {
+ byte curr = 0xFF;
+ long closest = ticker + 1000000, next = 0;
+
+ for (int i = 0; i < _track->channelCount; i++) {
+ if (_track->channels[i].time == -1) // channel ended
+ continue;
+ SoundResource::Channel *curChannel = &_track->channels[i];
+ if (curChannel->curPos >= curChannel->size)
+ continue;
+ next = curChannel->data[curChannel->curPos]; // when the next event should occur
+ if (next == 0xF8) // 0xF8 means 240 ticks delay
+ next = 240;
+ next += _track->channels[i].time;
+ if (next < closest) {
+ curr = i;
+ closest = next;
+ }
+ }
+
+ return curr;
+}
+
+byte *MidiParser_SCI::midiMixChannels() {
+ int totalSize = 0;
+
+ for (int i = 0; i < _track->channelCount; i++) {
+ _track->channels[i].time = 0;
+ _track->channels[i].prev = 0;
+ _track->channels[i].curPos = 0;
+ totalSize += _track->channels[i].size;
+ }
+
+ byte *outData = new byte[totalSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data
+ _mixedData = outData;
+ long ticker = 0;
+ byte channelNr, curDelta;
+ byte midiCommand = 0, midiParam, global_prev = 0;
+ long newDelta;
+ SoundResource::Channel *channel;
+
+ while ((channelNr = midiGetNextChannel(ticker)) != 0xFF) { // there is still an active channel
+ channel = &_track->channels[channelNr];
+ curDelta = channel->data[channel->curPos++];
+ channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the command is supposed to occur
+ if (curDelta == 0xF8)
+ continue;
+ newDelta = channel->time - ticker;
+ ticker += newDelta;
+
+ midiCommand = channel->data[channel->curPos++];
+ if (midiCommand != kEndOfTrack) {
+ // Write delta
+ while (newDelta > 240) {
+ *outData++ = 0xF8;
+ newDelta -= 240;
+ }
+ *outData++ = (byte)newDelta;
+ }
+ // Write command
+ switch (midiCommand) {
+ case 0xF0: // sysEx
+ *outData++ = midiCommand;
+ do {
+ midiParam = channel->data[channel->curPos++];
+ *outData++ = midiParam;
+ } while (midiParam != 0xF7);
+ break;
+ case kEndOfTrack: // end of channel
+ channel->time = -1;
+ break;
+ default: // MIDI command
+ if (midiCommand & 0x80) {
+ midiParam = channel->data[channel->curPos++];
+ } else {// running status
+ midiParam = midiCommand;
+ midiCommand = channel->prev;
+ }
+
+ // remember which channel got used for channel remapping
+ byte midiChannel = midiCommand & 0xF;
+ _channelUsed[midiChannel] = true;
+
+ if (midiCommand != global_prev)
+ *outData++ = midiCommand;
+ *outData++ = midiParam;
+ if (nMidiParams[(midiCommand >> 4) - 8] == 2)
+ *outData++ = channel->data[channel->curPos++];
+ channel->prev = midiCommand;
+ global_prev = midiCommand;
+ }
+ }
+
+ // Insert stop event
+ *outData++ = 0; // Delta
+ *outData++ = 0xFF; // Meta event
+ *outData++ = 0x2F; // End of track (EOT)
+ *outData++ = 0x00;
+ *outData++ = 0x00;
+ return _mixedData;
+}
+
+// This is used for SCI0 sound-data. SCI0 only has one stream that may
+// contain several channels and according to output device we remove
+// certain channels from that data.
+byte *MidiParser_SCI::midiFilterChannels(int channelMask) {
+ SoundResource::Channel *channel = &_track->channels[0];
+ byte *channelData = channel->data;
+ byte *channelDataEnd = channel->data + channel->size;
+ byte *outData = new byte[channel->size + 5];
+ byte curChannel = 15, curByte, curDelta;
+ byte command = 0, lastCommand = 0;
+ int delta = 0;
+ int midiParamCount = 0;
+
+ _mixedData = outData;
+
+ while (channelData < channelDataEnd) {
+ curDelta = *channelData++;
+ if (curDelta == 0xF8) {
+ delta += 240;
+ continue;
+ }
+ delta += curDelta;
+ curByte = *channelData++;
+
+ switch (curByte) {
+ case 0xF0: // sysEx
+ case kEndOfTrack: // end of channel
+ command = curByte;
+ curChannel = 15;
+ break;
+ default:
+ if (curByte & 0x80) {
+ command = curByte;
+ curChannel = command & 0x0F;
+ midiParamCount = nMidiParams[(command >> 4) - 8];
+ }
+ }
+ if ((1 << curChannel) & channelMask) {
+ if (command != kEndOfTrack) {
+ // Write delta
+ while (delta > 240) {
+ *outData++ = 0xF8;
+ delta -= 240;
+ }
+ *outData++ = (byte)delta;
+ delta = 0;
+ }
+ // Write command
+ switch (command) {
+ case 0xF0: // sysEx
+ *outData++ = command;
+ do {
+ curByte = *channelData++;
+ *outData++ = curByte; // out
+ } while (curByte != 0xF7);
+ lastCommand = command;
+ break;
+
+ case kEndOfTrack: // end of channel
+ break;
+
+ default: // MIDI command
+ // remember which channel got used for channel remapping
+ byte midiChannel = command & 0xF;
+ _channelUsed[midiChannel] = true;
+
+ if (lastCommand != command) {
+ *outData++ = command;
+ lastCommand = command;
+ }
+ if (midiParamCount > 0) {
+ if (curByte & 0x80)
+ *outData++ = *channelData++;
+ else
+ *outData++ = curByte;
+ }
+ if (midiParamCount > 1) {
+ *outData++ = *channelData++;
+ }
+ }
+ } else {
+ if (curByte & 0x80)
+ channelData += midiParamCount;
+ else
+ channelData += midiParamCount - 1;
+ }
+ }
+
+ // Insert stop event
+ *outData++ = 0; // Delta
+ *outData++ = 0xFF; // Meta event
+ *outData++ = 0x2F; // End of track (EOT)
+ *outData++ = 0x00;
+ *outData++ = 0x00;
+
+ return _mixedData;
+}
+
+// This will get called right before actual playing and will try to own the used channels
+void MidiParser_SCI::tryToOwnChannels() {
+ // We don't have SciMusic in case debug command show_instruments is used
+ if (!_music)
+ return;
+ for (int curChannel = 0; curChannel < 15; curChannel++) {
+ if (_channelUsed[curChannel]) {
+ if (_channelRemap[curChannel] == -1) {
+ _channelRemap[curChannel] = _music->tryToOwnChannel(_pSnd, curChannel);
+ }
+ }
+ }
+}
+
+void MidiParser_SCI::lostChannels() {
+ for (int curChannel = 0; curChannel < 15; curChannel++)
+ if ((_channelUsed[curChannel]) && (curChannel != 9))
+ _channelRemap[curChannel] = -1;
+}
+
+void MidiParser_SCI::sendInitCommands() {
+ // reset our "global" volume and channel volumes
+ _volume = 127;
+ for (int i = 0; i < 16; i++)
+ _channelVolume[i] = 127;
+
+ // Set initial voice count
+ if (_pSnd) {
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ for (int i = 0; i < 15; ++i) {
+ byte voiceCount = 0;
+ if (_channelUsed[i]) {
+ voiceCount = _pSnd->soundRes->getInitialVoiceCount(i);
+ sendToDriver(0xB0 | i, 0x4B, voiceCount);
+ }
+ }
}
}
// Send a velocity off signal to all channels
- for (int i = 0; i < 16; ++i) {
- _driver->send(0xB0 | i, 0x4E, 0); // Reset velocity
+ for (int i = 0; i < 15; ++i) {
+ if (_channelUsed[i])
+ sendToDriver(0xB0 | i, 0x4E, 0); // Reset velocity
}
- return true;
+ // Center the pitch wheels and hold pedal in preparation for the next piece of music
+ for (int i = 0; i < 16; ++i) {
+ if (_channelUsed[i]) {
+ sendToDriver(0xE0 | i, 0, 0x40); // Reset pitch wheel
+ sendToDriver(0xB0 | i, 0x40, 0); // Reset hold pedal
+ }
+ }
}
void MidiParser_SCI::unloadMusic() {
- resetTracking();
- allNotesOff();
+ if (_pSnd) {
+ resetTracking();
+ allNotesOff();
+ }
_num_tracks = 0;
_active_track = 255;
_resetOnPause = false;
@@ -116,33 +376,77 @@ void MidiParser_SCI::unloadMusic() {
delete[] _mixedData;
_mixedData = NULL;
}
+}
- // Center the pitch wheels and hold pedal in preparation for the next piece of music
- if (_driver) {
- for (int i = 0; i < 16; ++i) {
- if (_channelsUsed & (1 << i)) {
- _driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel
- _driver->send(0xB0 | i, 0x40, 0); // Reset hold pedal
- }
+// this is used for scripts sending midi commands to us. we verify in that case that the channel is actually
+// used, so that channel remapping will work as well and then send them on
+void MidiParser_SCI::sendFromScriptToDriver(uint32 midi) {
+ byte midiChannel = midi & 0xf;
+
+ if (!_channelUsed[midiChannel]) {
+ // trying to send to an unused channel
+ // this happens for cmdSendMidi at least in sq1vga right at the start, it's a script issue
+ return;
+ }
+ if (_channelRemap[midiChannel] == -1) {
+ // trying to send to an unmapped channel
+ // this happens for cmdSendMidi at least in sq1vga right at the start, scripts are pausing the sound
+ // and then sending manually. it's a script issue
+ return;
+ }
+ sendToDriver(midi);
+}
+
+void MidiParser_SCI::sendToDriver(uint32 midi) {
+ byte midiChannel = midi & 0xf;
+
+ if ((midi & 0xFFF0) == 0x4EB0) {
+ // this is channel mute only for sci1
+ // it's velocity control for sci0
+ if (_soundVersion >= SCI_VERSION_1_EARLY) {
+ _channelMuted[midiChannel] = midi & 0xFF0000 ? true : false;
+ return; // don't send this to driver at all
}
}
+
+ // Is channel muted? if so, don't send command
+ if (_channelMuted[midiChannel])
+ return;
+
+ if ((midi & 0xFFF0) == 0x07B0) {
+ // someone trying to set channel volume?
+ int channelVolume = (midi >> 16) & 0xFF;
+ // Remember, if we need to set it ourselves
+ _channelVolume[midiChannel] = channelVolume;
+ // Adjust volume accordingly to current "global" volume
+ channelVolume = channelVolume * _volume / 127;
+ midi = (midi & 0xFFF0) | ((channelVolume & 0xFF) << 16);
+ }
+
+ // Channel remapping
+ int16 realChannel = _channelRemap[midiChannel];
+ if (realChannel == -1)
+ return;
+
+ midi = (midi & 0xFFFFFFF0) | realChannel;
+ if (_mainThreadCalled)
+ _music->putMidiCommandInQueue(midi);
+ else
+ _driver->send(midi);
}
void MidiParser_SCI::parseNextEvent(EventInfo &info) {
- // Monitor which channels are used by this song
- _channelsUsed |= (1 << info.channel());
-
// Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs
if (_dataincAdd) {
_dataincAdd = false;
_pSnd->dataInc += _dataincToAdd;
_pSnd->signal = 0x7f + _pSnd->dataInc;
- debugC(2, kDebugLevelSound, "datainc %04x", _dataincToAdd);
+ debugC(4, kDebugLevelSound, "datainc %04x", _dataincToAdd);
}
if (_signalSet) {
_signalSet = false;
_pSnd->signal = _signalToSet;
- debugC(2, kDebugLevelSound, "signal %04x", _signalToSet);
+ debugC(4, kDebugLevelSound, "signal %04x", _signalToSet);
}
info.start = _position._play_pos;
@@ -168,10 +472,16 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
info.basic.param2 = 0;
if (info.channel() == 0xF) {// SCI special case
if (info.basic.param1 != kSetSignalLoop) {
- _signalSet = true;
- _signalToSet = info.basic.param1;
+ // at least in kq5/french&mac the first scene in the intro has a song that sets signal to 4 immediately
+ // on tick 0. Signal isn't set at that point by sierra sci and it would cause the castle daventry text to
+ // get immediately removed, so we currently filter it.
+ // Sierra SCI ignores them as well at that time
+ if ((_position._play_tick) || (info.delta)) {
+ _signalSet = true;
+ _signalToSet = info.basic.param1;
+ }
} else {
- _loopTick = _position._play_tick;
+ _loopTick = _position._play_tick + info.delta;
}
}
break;
@@ -207,10 +517,11 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
break;
case SCI_VERSION_1_EARLY:
case SCI_VERSION_1_LATE:
+ case SCI_VERSION_2_1:
_dataincToAdd = 1;
break;
default:
- break;
+ error("unsupported _soundVersion");
}
break;
case kResetOnPause:
@@ -230,7 +541,6 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
case 0x0A: // pan
case 0x0B: // expression
case 0x40: // sustain
- case 0x4E: // velocity control
case 0x79: // reset all
case 0x7B: // notes off
// These are all handled by the music driver, so ignore them
@@ -244,8 +554,6 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
break;
}
}
- if (info.basic.param1 == 7) // channel volume change -scale it
- info.basic.param2 = info.basic.param2 * _volume / MUSIC_VOLUME_MAX;
info.length = 0;
break;
@@ -302,7 +610,7 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
_pSnd->status = kSoundStopped;
_pSnd->signal = SIGNAL_OFFSET;
- debugC(2, kDebugLevelSound, "signal EOT");
+ debugC(4, kDebugLevelSound, "signal EOT");
}
}
break;
@@ -314,245 +622,67 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
}// switch (info.command())
}
+void MidiParser_SCI::allNotesOff() {
+ if (!_driver)
+ return;
-byte MidiParser_SCI::midiGetNextChannel(long ticker) {
- byte curr = 0xFF;
- long closest = ticker + 1000000, next = 0;
-
- for (int i = 0; i < _track->channelCount; i++) {
- if (_track->channels[i].time == -1) // channel ended
- continue;
- next = *_track->channels[i].data; // when the next event shoudl occur
- if (next == 0xF8) // 0xF8 means 240 ticks delay
- next = 240;
- next += _track->channels[i].time;
- if (next < closest) {
- curr = i;
- closest = next;
- }
- }
-
- return curr;
-}
-
-byte *MidiParser_SCI::midiMixChannels() {
- int totalSize = 0;
- byte **dataPtr = new byte *[_track->channelCount];
-
- for (int i = 0; i < _track->channelCount; i++) {
- dataPtr[i] = _track->channels[i].data;
- _track->channels[i].time = 0;
- _track->channels[i].prev = 0;
- totalSize += _track->channels[i].size;
- }
-
- byte *outData = new byte[totalSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data
- _mixedData = outData;
- long ticker = 0;
- byte curr, curDelta;
- byte command = 0, par1, global_prev = 0;
- long new_delta;
- SoundResource::Channel *channel;
-
- while ((curr = midiGetNextChannel(ticker)) != 0xFF) { // there is still active channel
- channel = &_track->channels[curr];
- curDelta = *channel->data++;
- channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the comamnd is supposed to occur
- if (curDelta == 0xF8)
- continue;
- new_delta = channel->time - ticker;
- ticker += new_delta;
+ int i, j;
- command = *channel->data++;
- if (command != kEndOfTrack) {
- debugC(2, kDebugLevelSound, "\nDELTA ");
- // Write delta
- while (new_delta > 240) {
- *outData++ = 0xF8;
- debugC(2, kDebugLevelSound, "F8 ");
- new_delta -= 240;
- }
- *outData++ = (byte)new_delta;
- debugC(2, kDebugLevelSound, "%02X ", (uint32)new_delta);
- }
- // Write command
- switch (command) {
- case 0xF0: // sysEx
- *outData++ = command;
- debugC(2, kDebugLevelSound, "%02X ", command);
- do {
- par1 = *channel->data++;
- *outData++ = par1; // out
- } while (par1 != 0xF7);
- break;
- case kEndOfTrack: // end of channel
- channel->time = -1; // FIXME
- break;
- default: // MIDI command
- if (command & 0x80)
- par1 = *channel->data++;
- else {// running status
- par1 = command;
- command = channel->prev;
+ // Turn off all active notes
+ for (i = 0; i < 128; ++i) {
+ for (j = 0; j < 16; ++j) {
+ if ((_active_notes[i] & (1 << j)) && (_channelRemap[j] != -1)){
+ sendToDriver(0x80 | j, i, 0);
}
- if (command != global_prev)
- *outData++ = command; // out command
- *outData++ = par1;// pout par1
- if (nMidiParams[(command >> 4) - 8] == 2)
- *outData++ = *channel->data++; // out par2
- channel->prev = command;
- global_prev = command;
- }// switch(command)
- }// while (curr)
-
- // Insert stop event
- *outData++ = 0; // Delta
- *outData++ = 0xFF; // Meta event
- *outData++ = 0x2F; // End of track (EOT)
- *outData++ = 0x00;
- *outData++ = 0x00;
-
- for (int channelNr = 0; channelNr < _track->channelCount; channelNr++)
- _track->channels[channelNr].data = dataPtr[channelNr];
-
- delete[] dataPtr;
- return _mixedData;
-}
-
-// This is used for SCI0 sound-data. SCI0 only has one stream that may
-// contain several channels and according to output device we remove
-// certain channels from that data.
-byte *MidiParser_SCI::midiFilterChannels(int channelMask) {
- SoundResource::Channel *channel = &_track->channels[0];
- byte *channelData = channel->data;
- byte *channelDataEnd = channel->data + channel->size;
- byte *outData = new byte[channel->size + 5];
- byte curChannel = 15, curByte, curDelta;
- byte command = 0, lastCommand = 0;
- int delta = 0;
- int midiParamCount = 0;
-
- _mixedData = outData;
-
- while (channelData < channelDataEnd) {
- curDelta = *channelData++;
- if (curDelta == 0xF8) {
- delta += 240;
- continue;
}
- delta += curDelta;
- curByte = *channelData++;
+ }
- switch (curByte) {
- case 0xF0: // sysEx
- case kEndOfTrack: // end of channel
- command = curByte;
- curChannel = 15;
- break;
- default:
- if (curByte & 0x80) {
- command = curByte;
- curChannel = command & 0x0F;
- midiParamCount = nMidiParams[(command >> 4) - 8];
- }
+ // Turn off all hanging notes
+ for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) {
+ byte midiChannel = _hanging_notes[i].channel;
+ if ((_hanging_notes[i].time_left) && (_channelRemap[midiChannel] != -1)) {
+ sendToDriver(0x80 | midiChannel, _hanging_notes[i].note, 0);
+ _hanging_notes[i].time_left = 0;
}
- if ((1 << curChannel) & channelMask) {
- if (command != kEndOfTrack) {
- debugC(2, kDebugLevelSound, "\nDELTA ");
- // Write delta
- while (delta > 240) {
- *outData++ = 0xF8;
- debugC(2, kDebugLevelSound, "F8 ");
- delta -= 240;
- }
- *outData++ = (byte)delta;
- debugC(2, kDebugLevelSound, "%02X ", delta);
- delta = 0;
- }
- // Write command
- switch (command) {
- case 0xF0: // sysEx
- *outData++ = command;
- debugC(2, kDebugLevelSound, "%02X ", command);
- do {
- curByte = *channelData++;
- *outData++ = curByte; // out
- } while (curByte != 0xF7);
- lastCommand = command;
- break;
+ }
+ _hanging_notes_count = 0;
- case kEndOfTrack: // end of channel
- break;
+ // To be sure, send an "All Note Off" event (but not all MIDI devices
+ // support this...).
- default: // MIDI command
- if (lastCommand != command) {
- *outData++ = command;
- debugC(2, kDebugLevelSound, "%02X ", command);
- lastCommand = command;
- }
- if (midiParamCount > 0) {
- if (curByte & 0x80) {
- debugC(2, kDebugLevelSound, "%02X ", *channelData);
- *outData++ = *channelData++;
- } else {
- debugC(2, kDebugLevelSound, "%02X ", curByte);
- *outData++ = curByte;
- }
- }
- if (midiParamCount > 1) {
- debugC(2, kDebugLevelSound, "%02X ", *channelData);
- *outData++ = *channelData++;
- }
- }
- } else {
- if (curByte & 0x80) {
- channelData += midiParamCount;
- } else {
- channelData += midiParamCount - 1;
- }
- }
+ for (i = 0; i < 16; ++i) {
+ if (_channelRemap[i] != -1)
+ sendToDriver(0xB0 | i, 0x7b, 0); // All notes off
}
- // Insert stop event
- *outData++ = 0; // Delta
- *outData++ = 0xFF; // Meta event
- *outData++ = 0x2F; // End of track (EOT)
- *outData++ = 0x00;
- *outData++ = 0x00;
-
- return _mixedData;
+ memset(_active_notes, 0, sizeof(_active_notes));
}
void MidiParser_SCI::setVolume(byte volume) {
- // FIXME: This receives values > 127... throw a warning for now and clip the variable
- if (volume > MUSIC_VOLUME_MAX) {
- warning("attempted to set an invalid volume(%d)", volume);
- volume = MUSIC_VOLUME_MAX; // reset
- }
-
assert(volume <= MUSIC_VOLUME_MAX);
- if (_volume != volume) {
- _volume = volume;
-
- switch (_soundVersion) {
- case SCI_VERSION_0_EARLY:
- case SCI_VERSION_0_LATE: {
- int16 globalVolume = _volume * 15 / 127;
- ((MidiPlayer *)_driver)->setVolume(globalVolume);
- break;
- }
+ _volume = volume;
+
+ switch (_soundVersion) {
+ case SCI_VERSION_0_EARLY:
+ case SCI_VERSION_0_LATE: {
+ // SCI0 adlib driver doesn't support channel volumes, so we need to go this way
+ // TODO: this should take the actual master volume into account
+ int16 globalVolume = _volume * 15 / 127;
+ ((MidiPlayer *)_driver)->setVolume(globalVolume);
+ break;
+ }
- case SCI_VERSION_1_EARLY:
- case SCI_VERSION_1_LATE:
- // sending volume change to all active channels
- for (int i = 0; i < _track->channelCount; i++)
- if (_track->channels[i].number <= 0xF)
- _driver->send(0xB0 + _track->channels[i].number, 7, _volume);
- break;
+ case SCI_VERSION_1_EARLY:
+ case SCI_VERSION_1_LATE:
+ case SCI_VERSION_2_1:
+ // Send previous channel volumes again to actually update the volume
+ for (int i = 0; i < 15; i++)
+ if (_channelRemap[i] != -1)
+ sendToDriver(0xB0 + i, 7, _channelVolume[i]);
+ break;
- default:
- error("MidiParser_SCI::setVolume: Unsupported soundVersion");
- }
+ default:
+ error("MidiParser_SCI::setVolume: Unsupported soundVersion");
}
}
diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h
index f95c71ce2f..90db06e539 100644
--- a/engines/sci/sound/midiparser_sci.h
+++ b/engines/sci/sound/midiparser_sci.h
@@ -53,12 +53,17 @@ namespace Sci {
*/
class MidiParser_SCI : public MidiParser {
public:
- MidiParser_SCI(SciVersion soundVersion);
+ MidiParser_SCI(SciVersion soundVersion, SciMusic *music);
~MidiParser_SCI();
+
+ void mainThreadBegin();
+ void mainThreadEnd();
+
bool loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion);
bool loadMusic(byte *, uint32) {
return false;
}
+ void sendInitCommands();
void unloadMusic();
void setVolume(byte volume);
void stop() {
@@ -71,12 +76,29 @@ public:
jumpToTick(0);
}
+ void allNotesOff();
+
+ const byte *getMixedData() const { return _mixedData; }
+
+ void tryToOwnChannels();
+ void lostChannels();
+ void sendFromScriptToDriver(uint32 midi);
+ void sendToDriver(uint32 midi);
+ void sendToDriver(byte status, byte firstOp, byte secondOp) {
+ sendToDriver(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16));
+ }
+
protected:
void parseNextEvent(EventInfo &info);
byte *midiMixChannels();
byte *midiFilterChannels(int channelMask);
byte midiGetNextChannel(long ticker);
+ SciMusic *_music;
+
+ // this is set, when main thread calls us -> we send commands to queue instead to driver
+ bool _mainThreadCalled;
+
SciVersion _soundVersion;
byte *_mixedData;
SoundResource::Track *_track;
@@ -90,9 +112,10 @@ protected:
int16 _dataincToAdd;
bool _resetOnPause;
- // A 16-bit mask, containing the channels used
- // by the currently parsed song
- uint16 _channelsUsed;
+ bool _channelUsed[16];
+ int16 _channelRemap[16];
+ bool _channelMuted[16];
+ byte _channelVolume[16];
};
} // End of namespace Sci
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 66f5ce9710..061f380ebc 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -37,15 +37,17 @@
namespace Sci {
-// When defined, volume fading immediately sets the final sound volume
-#define DISABLE_VOLUME_FADING
-
SciMusic::SciMusic(SciVersion soundVersion)
: _soundVersion(soundVersion), _soundOn(true), _masterVolume(0) {
// Reserve some space in the playlist, to avoid expensive insertion
// operations
_playList.reserve(10);
+
+ for (int i = 0; i < 16; i++)
+ _usedChannel[i] = 0;
+
+ _queuedCommands.reserve(1000);
}
SciMusic::~SciMusic() {
@@ -61,31 +63,22 @@ void SciMusic::init() {
// SCI sound init
_dwTempo = 0;
- MidiDriverType midiType;
-
- // Default to MIDI in SCI32 games, as many don't have AdLib support.
- // WORKAROUND: Default to MIDI in Amiga SCI1_EGA+ games as we don't support those patches yet.
- // We also don't yet support the 7.pat file of SCI1+ Mac games or SCI0 Mac patches, so we
- // default to MIDI in those games to let them run.
+ // Default to MIDI in SCI2.1+ games, as many don't have AdLib support.
Common::Platform platform = g_sci->getPlatform();
+ uint32 dev = MidiDriver::detectDevice((getSciVersion() >= SCI_VERSION_2_1) ? (MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM) : (MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI));
- if (getSciVersion() >= SCI_VERSION_2 || platform == Common::kPlatformMacintosh || (platform == Common::kPlatformAmiga && getSciVersion() >= SCI_VERSION_1_EGA))
- midiType = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MIDI);
- else
- midiType = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI);
-
- switch (midiType) {
- case MD_ADLIB:
+ switch (MidiDriver::getMusicType(dev)) {
+ case MT_ADLIB:
// FIXME: There's no Amiga sound option, so we hook it up to AdLib
- if (g_sci->getPlatform() == Common::kPlatformAmiga)
- _pMidiDrv = MidiPlayer_Amiga_create(_soundVersion);
+ if (g_sci->getPlatform() == Common::kPlatformAmiga || platform == Common::kPlatformMacintosh)
+ _pMidiDrv = MidiPlayer_AmigaMac_create(_soundVersion);
else
_pMidiDrv = MidiPlayer_AdLib_create(_soundVersion);
break;
- case MD_PCJR:
+ case MT_PCJR:
_pMidiDrv = MidiPlayer_PCJr_create(_soundVersion);
break;
- case MD_PCSPK:
+ case MT_PCSPK:
_pMidiDrv = MidiPlayer_PCSpeaker_create(_soundVersion);
break;
default:
@@ -103,6 +96,49 @@ void SciMusic::init() {
}
_bMultiMidi = ConfMan.getBool("multi_midi");
+
+ // Find out what the first possible channel is (used, when doing channel
+ // remapping).
+ _driverFirstChannel = _pMidiDrv->getFirstChannel();
+}
+
+void SciMusic::miditimerCallback(void *p) {
+ SciMusic *sciMusic = (SciMusic *)p;
+
+ Common::StackLock lock(sciMusic->_mutex);
+ sciMusic->onTimer();
+}
+
+void SciMusic::onTimer() {
+ const MusicList::iterator end = _playList.end();
+ // sending out queued commands that were "sent" via main thread
+ sendMidiCommandsFromQueue();
+
+ for (MusicList::iterator i = _playList.begin(); i != end; ++i)
+ (*i)->onTimer();
+}
+
+void SciMusic::putMidiCommandInQueue(byte status, byte firstOp, byte secondOp) {
+ putMidiCommandInQueue(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16));
+}
+
+void SciMusic::putMidiCommandInQueue(uint32 midi) {
+ _queuedCommands.push_back(midi);
+}
+
+// This sends the stored commands from queue to driver (is supposed to get
+// called only during onTimer()). At least mt32 emulation doesn't like getting
+// note-on commands from main thread (if we directly send, we would get a crash
+// during piano scene in lsl5).
+void SciMusic::sendMidiCommandsFromQueue() {
+ uint curCommand = 0;
+ uint commandCount = _queuedCommands.size();
+
+ while (curCommand < commandCount) {
+ _pMidiDrv->send(_queuedCommands[curCommand]);
+ curCommand++;
+ }
+ _queuedCommands.clear();
}
void SciMusic::clearPlayList() {
@@ -115,8 +151,6 @@ void SciMusic::clearPlayList() {
}
void SciMusic::pauseAll(bool pause) {
- Common::StackLock lock(_mutex);
-
const MusicList::iterator end = _playList.end();
for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
soundToggle(*i, pause);
@@ -124,22 +158,12 @@ void SciMusic::pauseAll(bool pause) {
}
void SciMusic::stopAll() {
- Common::StackLock lock(_mutex);
-
const MusicList::iterator end = _playList.end();
for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
soundStop(*i);
}
}
-
-void SciMusic::miditimerCallback(void *p) {
- SciMusic *aud = (SciMusic *)p;
-
- Common::StackLock lock(aud->_mutex);
- aud->onTimer();
-}
-
void SciMusic::soundSetSoundOn(bool soundOnFlag) {
Common::StackLock lock(_mutex);
@@ -165,27 +189,46 @@ MusicEntry *SciMusic::getSlot(reg_t obj) {
return NULL;
}
+// We return the currently active music slot for SCI0
+MusicEntry *SciMusic::getActiveSci0MusicSlot() {
+ const MusicList::iterator end = _playList.end();
+ MusicEntry *highestPrioritySlot = NULL;
+ for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
+ MusicEntry *playSlot = *i;
+ if (playSlot->pMidiParser) {
+ if (playSlot->status == kSoundPlaying)
+ return playSlot;
+ if (playSlot->status == kSoundPaused) {
+ if ((!highestPrioritySlot) || (highestPrioritySlot->priority < playSlot->priority))
+ highestPrioritySlot = playSlot;
+ }
+ }
+ }
+ return highestPrioritySlot;
+}
+
void SciMusic::setReverb(byte reverb) {
Common::StackLock lock(_mutex);
_pMidiDrv->setReverb(reverb);
}
-static int f_compare(const void *arg1, const void *arg2) {
- return ((const MusicEntry *)arg2)->priority - ((const MusicEntry *)arg1)->priority;
+static bool musicEntryCompare(const MusicEntry *l, const MusicEntry *r) {
+ return (l->priority > r->priority);
}
void SciMusic::sortPlayList() {
- MusicEntry ** pData = _playList.begin();
- qsort(pData, _playList.size(), sizeof(MusicEntry *), &f_compare);
+ // Sort the play list in descending priority order
+ Common::sort(_playList.begin(), _playList.end(), musicEntryCompare);
}
+
void SciMusic::soundInitSnd(MusicEntry *pSnd) {
int channelFilterMask = 0;
SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId());
- // If MIDI device is selected but there is no digital track in sound resource
- // try to use adlib's digital sample if possible
- // Also, if the track couldn't be found, load the digital track, as some games
- // depend on this (e.g. the Longbow demo)
+ // If MIDI device is selected but there is no digital track in sound
+ // resource try to use adlib's digital sample if possible. Also, if the
+ // track couldn't be found, load the digital track, as some games depend on
+ // this (e.g. the Longbow demo).
if (!track || (_bMultiMidi && track->digitalChannelNr == -1)) {
SoundResource::Track *digital = pSnd->soundRes->getDigitalTrack();
if (digital)
@@ -214,7 +257,7 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
_mutex.lock();
pSnd->soundType = Audio::Mixer::kMusicSoundType;
if (pSnd->pMidiParser == NULL) {
- pSnd->pMidiParser = new MidiParser_SCI(_soundVersion);
+ pSnd->pMidiParser = new MidiParser_SCI(_soundVersion, this);
pSnd->pMidiParser->setMidiDriver(_pMidiDrv);
pSnd->pMidiParser->setTimerRate(_dwTempo);
}
@@ -223,19 +266,47 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
// Find out what channels to filter for SCI0
channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel());
- pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
- // Fast forward to the last position and perform associated events when loading
- pSnd->pMidiParser->jumpToTick(pSnd->ticker, true);
+ pSnd->pMidiParser->mainThreadBegin();
+ pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
+ pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
}
}
}
-void SciMusic::onTimer() {
- const MusicList::iterator end = _playList.end();
- for (MusicList::iterator i = _playList.begin(); i != end; ++i)
- (*i)->onTimer();
+// This one checks, if requested channel is available -> in that case give
+// caller that channel. Otherwise look for an unused one
+int16 SciMusic::tryToOwnChannel(MusicEntry *caller, int16 bestChannel) {
+ // Don't even try this for SCI0
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return bestChannel;
+ if (!_usedChannel[bestChannel]) {
+ // currently unused, so give it to caller directly
+ _usedChannel[bestChannel] = caller;
+ return bestChannel;
+ }
+ // otherwise look for unused channel
+ for (int channelNr = _driverFirstChannel; channelNr < 15; channelNr++) {
+ if (!_usedChannel[channelNr]) {
+ _usedChannel[channelNr] = caller;
+ return channelNr;
+ }
+ }
+ // nothing found, don't map channel at all
+ // sierra did this as well, although i'm not sure if we act exactly the same way
+ // maybe they removed channels from previous playing music
+ return -1;
+}
+
+void SciMusic::freeChannels(MusicEntry *caller) {
+ // Remove used channels
+ for (int i = 0; i < 15; i++) {
+ if (_usedChannel[i] == caller)
+ _usedChannel[i] = 0;
+ }
+ // Also tell midiparser, that he lost ownership
+ caller->pMidiParser->lostChannels();
}
void SciMusic::soundPlay(MusicEntry *pSnd) {
@@ -243,14 +314,14 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
uint playListCount = _playList.size();
uint playListNo = playListCount;
- bool alreadyPlaying = false;
+ MusicEntry *alreadyPlaying = NULL;
// searching if sound is already in _playList
for (uint i = 0; i < playListCount; i++) {
if (_playList[i] == pSnd)
playListNo = i;
if ((_playList[i]->status == kSoundPlaying) && (_playList[i]->pMidiParser))
- alreadyPlaying = true;
+ alreadyPlaying = _playList[i];
}
if (playListNo == playListCount) { // not found
_playList.push_back(pSnd);
@@ -261,13 +332,20 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
if (pSnd->pMidiParser) {
if ((_soundVersion <= SCI_VERSION_0_LATE) && (alreadyPlaying)) {
- // if any music is already playing, SCI0 queues music and plays it after the current music has finished
- // done by SoundCommandParser::updateSci0Cues()
- // Example of such case: iceman room 14
- // FIXME: this code is supposed to also take a look at priority and pause currently playing sound accordingly
- pSnd->isQueued = true;
- pSnd->status = kSoundPaused;
- return;
+ // Music already playing in SCI0?
+ if (pSnd->priority > alreadyPlaying->priority) {
+ // And new priority higher? pause previous music and play new one immediately.
+ // Example of such case: lsl3, when getting points (jingle is played then)
+ soundPause(alreadyPlaying);
+ alreadyPlaying->isQueued = true;
+ } else {
+ // And new priority equal or lower? queue up music and play it afterwards done by
+ // SoundCommandParser::updateSci0Cues()
+ // Example of such case: iceman room 14
+ pSnd->isQueued = true;
+ pSnd->status = kSoundPaused;
+ return;
+ }
}
}
@@ -279,32 +357,56 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
pSnd->pLoopStream, -1, pSnd->volume, 0,
DisposeAfterUse::NO);
} else {
+ // Rewind in case we play the same sample multiple times
+ // (non-looped) like in pharkas right at the start
+ pSnd->pStreamAud->rewind();
_pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud,
pSnd->pStreamAud, -1, pSnd->volume, 0,
DisposeAfterUse::NO);
}
} else {
- _mutex.lock();
if (pSnd->pMidiParser) {
- pSnd->pMidiParser->setVolume(pSnd->volume);
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
+ pSnd->pMidiParser->tryToOwnChannels();
if (pSnd->status == kSoundStopped)
+ pSnd->pMidiParser->sendInitCommands();
+ pSnd->pMidiParser->setVolume(pSnd->volume);
+ if (pSnd->status == kSoundStopped) {
pSnd->pMidiParser->jumpToTick(0);
+ } else {
+ // Fast forward to the last position and perform associated events when loading
+ pSnd->pMidiParser->jumpToTick(pSnd->ticker, true);
+ }
+ pSnd->pMidiParser->mainThreadEnd();
+ _mutex.unlock();
}
- _mutex.unlock();
}
pSnd->status = kSoundPlaying;
}
void SciMusic::soundStop(MusicEntry *pSnd) {
+ SoundStatus previousStatus = pSnd->status;
pSnd->status = kSoundStopped;
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ pSnd->isQueued = false;
if (pSnd->pStreamAud)
_pMixer->stopHandle(pSnd->hCurrentAud);
- _mutex.lock();
- if (pSnd->pMidiParser)
- pSnd->pMidiParser->stop();
- _mutex.unlock();
+ if (pSnd->pMidiParser) {
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
+ // We shouldn't call stop in case it's paused, otherwise we would send
+ // allNotesOff() again
+ if (previousStatus == kSoundPlaying)
+ pSnd->pMidiParser->stop();
+ freeChannels(pSnd);
+ pSnd->pMidiParser->mainThreadEnd();
+ _mutex.unlock();
+ }
+
+ pSnd->fadeStep = 0; // end fading, if fading was in progress
}
void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) {
@@ -313,7 +415,9 @@ void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) {
_pMixer->setChannelVolume(pSnd->hCurrentAud, volume * 2); // Mixer is 0-255, SCI is 0-127
} else if (pSnd->pMidiParser) {
_mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
pSnd->pMidiParser->setVolume(volume);
+ pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
}
}
@@ -328,13 +432,15 @@ void SciMusic::soundSetPriority(MusicEntry *pSnd, byte prio) {
void SciMusic::soundKill(MusicEntry *pSnd) {
pSnd->status = kSoundStopped;
- _mutex.lock();
if (pSnd->pMidiParser) {
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
pSnd->pMidiParser->unloadMusic();
+ pSnd->pMidiParser->mainThreadEnd();
delete pSnd->pMidiParser;
pSnd->pMidiParser = NULL;
+ _mutex.unlock();
}
- _mutex.unlock();
if (pSnd->pStreamAud) {
_pMixer->stopHandle(pSnd->hCurrentAud);
@@ -366,10 +472,14 @@ void SciMusic::soundPause(MusicEntry *pSnd) {
if (pSnd->pStreamAud) {
_pMixer->pauseHandle(pSnd->hCurrentAud, true);
} else {
- _mutex.lock();
- if (pSnd->pMidiParser)
+ if (pSnd->pMidiParser) {
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
pSnd->pMidiParser->pause();
- _mutex.unlock();
+ freeChannels(pSnd);
+ pSnd->pMidiParser->mainThreadEnd();
+ _mutex.unlock();
+ }
}
}
@@ -380,7 +490,12 @@ void SciMusic::soundResume(MusicEntry *pSnd) {
return;
if (pSnd->status != kSoundPaused)
return;
- soundPlay(pSnd);
+ if (pSnd->pStreamAud) {
+ _pMixer->pauseHandle(pSnd->hCurrentAud, false);
+ pSnd->status = kSoundPlaying;
+ } else {
+ soundPlay(pSnd);
+ }
}
void SciMusic::soundToggle(MusicEntry *pSnd, bool pause) {
@@ -403,6 +518,21 @@ void SciMusic::soundSetMasterVolume(uint16 vol) {
_pMidiDrv->setVolume(vol);
}
+void SciMusic::sendMidiCommand(uint32 cmd) {
+ Common::StackLock lock(_mutex);
+ _pMidiDrv->send(cmd);
+}
+
+void SciMusic::sendMidiCommand(MusicEntry *pSnd, uint32 cmd) {
+ Common::StackLock lock(_mutex);
+ if (!pSnd->pMidiParser)
+ error("tried to cmdSendMidi on non midi slot (%04x:%04x)", PRINT_REG(pSnd->soundObj));
+
+ pSnd->pMidiParser->mainThreadBegin();
+ pSnd->pMidiParser->sendFromScriptToDriver(cmd);
+ pSnd->pMidiParser->mainThreadEnd();
+}
+
void SciMusic::printPlayList(Console *con) {
Common::StackLock lock(_mutex);
@@ -471,7 +601,7 @@ MusicEntry::MusicEntry() {
priority = 0;
loop = 0;
volume = MUSIC_VOLUME_DEFAULT;
- hold = 0;
+ hold = -1;
pauseCounter = 0;
sampleLoopCounter = 0;
@@ -525,12 +655,6 @@ void MusicEntry::doFade() {
// Only process MIDI streams in this thread, not digital sound effects
if (pMidiParser) {
-#ifdef DISABLE_VOLUME_FADING
- // Signal fading to stop...
- volume = fadeTo;
- fadeStep = 0;
- fadeCompleted = true;
-#endif
pMidiParser->setVolume(volume);
}
diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h
index 8f08065b99..37e3c30030 100644
--- a/engines/sci/sound/music.h
+++ b/engines/sci/sound/music.h
@@ -26,15 +26,11 @@
#ifndef SCI_MUSIC_H
#define SCI_MUSIC_H
-#ifndef USE_OLD_MUSIC_FUNCTIONS
#include "common/serializer.h"
-#endif
#include "common/mutex.h"
#include "sound/mixer.h"
#include "sound/audiostream.h"
-//#include "sound/mididrv.h"
-//#include "sound/midiparser.h"
#include "sci/sci.h"
#include "sci/resource.h"
@@ -55,11 +51,7 @@ enum SoundStatus {
class MidiParser_SCI;
class SegManager;
-class MusicEntry
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- : public Common::Serializable
-#endif
-{
+class MusicEntry : public Common::Serializable {
public:
// Do not get these directly for the sound objects!
// It's a bad idea, as the sound code (i.e. the SciMusic
@@ -79,7 +71,7 @@ public:
byte priority;
uint16 loop;
int16 volume;
- byte hold;
+ int16 hold;
int16 pauseCounter;
uint sampleLoopCounter;
@@ -96,9 +88,6 @@ public:
Audio::Mixer::SoundType soundType;
-#ifndef USE_OLD_MUSIC_FUNCTIONS
-//protected:
-#endif
MidiParser_SCI *pMidiParser;
// TODO: We need to revise how we store the different
@@ -114,25 +103,28 @@ public:
void doFade();
void onTimer();
-#ifndef USE_OLD_MUSIC_FUNCTIONS
virtual void saveLoadWithSerializer(Common::Serializer &ser);
-#endif
};
typedef Common::Array<MusicEntry *> MusicList;
+typedef Common::Array<uint32> MidiCommandQueue;
-class SciMusic
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- : public Common::Serializable
-#endif
-{
+class SciMusic : public Common::Serializable {
public:
SciMusic(SciVersion soundVersion);
~SciMusic();
void init();
+
void onTimer();
+ void putMidiCommandInQueue(byte status, byte firstOp, byte secondOp);
+ void putMidiCommandInQueue(uint32 midi);
+private:
+ static void miditimerCallback(void *p);
+ void sendMidiCommandsFromQueue();
+
+public:
void clearPlayList();
void pauseAll(bool pause);
void stopAll();
@@ -165,6 +157,7 @@ public:
}
MusicEntry *getSlot(reg_t obj);
+ MusicEntry *getActiveSci0MusicSlot();
void pushBackSlot(MusicEntry *slotEntry) {
Common::StackLock lock(_mutex);
@@ -179,16 +172,12 @@ public:
MusicList::iterator getPlayListStart() { return _playList.begin(); }
MusicList::iterator getPlayListEnd() { return _playList.end(); }
- void sendMidiCommand(uint32 cmd) {
- Common::StackLock lock(_mutex);
- _pMidiDrv->send(cmd);
- }
+ void sendMidiCommand(uint32 cmd);
+ void sendMidiCommand(MusicEntry *pSnd, uint32 cmd);
void setReverb(byte reverb);
-#ifndef USE_OLD_MUSIC_FUNCTIONS
virtual void saveLoadWithSerializer(Common::Serializer &ser);
-#endif
// Mutex for music code. Used to guard access to the song playlist, to the
// MIDI parser and to the MIDI driver/player. Note that guarded code must NOT
@@ -196,8 +185,10 @@ public:
// where a deadlock can occur
Common::Mutex _mutex;
+ int16 tryToOwnChannel(MusicEntry *caller, int16 bestChannel);
+ void freeChannels(MusicEntry *caller);
+
protected:
- byte findAudEntry(uint16 nAud, byte&oVolume, uint32& oOffset, uint32&oSize);
void sortPlayList();
SciVersion _soundVersion;
@@ -209,12 +200,16 @@ protected:
// Mixed AdLib/MIDI mode: when enabled from the ScummVM sound options screen,
// and a sound has a digital track, the sound from the AdLib track is played
bool _bMultiMidi;
-private:
- static void miditimerCallback(void *p);
+private:
MusicList _playList;
bool _soundOn;
byte _masterVolume;
+ MusicEntry *_usedChannel[16];
+
+ MidiCommandQueue _queuedCommands;
+
+ int _driverFirstChannel;
};
} // End of namespace Sci
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index 925f3b2e1a..567a1605f3 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -23,276 +23,40 @@
*
*/
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/iterator.h" // for SongIteratorStatus
-#endif
-
#include "common/config-manager.h"
#include "sci/sound/audio.h"
#include "sci/sound/music.h"
#include "sci/sound/soundcmd.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
namespace Sci {
-#define SCI1_SOUND_FLAG_MAY_PAUSE 1 /* Only here for completeness; The interpreter doesn't touch this bit */
-#define SCI1_SOUND_FLAG_SCRIPTED_PRI 2 /* but does touch this */
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#define FROBNICATE_HANDLE(reg) ((reg).segment << 16 | (reg).offset)
-#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff))
-#endif
-
-#define SOUNDCOMMAND(x) _soundCommands.push_back(new MusicEntryCommand(#x, &SoundCommandParser::x))
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void script_set_priority(ResourceManager *resMan, SegManager *segMan, SfxState *state, reg_t obj, int priority) {
- int song_nr = GET_SEL32V(segMan, obj, SELECTOR(number));
- Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0);
- int flags = GET_SEL32V(segMan, obj, SELECTOR(flags));
-
- if (priority == -1) {
- if (song->data[0] == 0xf0)
- priority = song->data[1];
- else
- warning("Attempt to unset song priority when there is no built-in value");
-
- flags &= ~SCI1_SOUND_FLAG_SCRIPTED_PRI;
- } else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI;
-
- state->sfx_song_renice(FROBNICATE_HANDLE(obj), priority);
- PUT_SEL32V(segMan, obj, SELECTOR(flags), flags);
-}
-
-SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id) {
- Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0);
-
- if (!song)
- return NULL;
-
- return songit_new(song->data, song->size, type, id);
-}
-
-void process_sound_events(EngineState *s) { /* Get all sound events, apply their changes to the heap */
- int result;
- SongHandle handle;
- int cue;
- SegManager *segMan = s->_segMan;
-
- if (getSciVersion() > SCI_VERSION_01)
- return;
- // SCI1 and later explicitly poll for everything
-
- while ((result = s->_sound.sfx_poll(&handle, &cue))) {
- reg_t obj = DEFROBNICATE_HANDLE(handle);
- if (!s->_segMan->isObject(obj)) {
- warning("Non-object %04x:%04x received sound signal (%d/%d)", PRINT_REG(obj), result, cue);
- return;
- }
-
- switch (result) {
-
- case SI_LOOP:
- debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x looped (to %d)",
- PRINT_REG(obj), cue);
- /* PUT_SEL32V(segMan, obj, SELECTOR(loops), GET_SEL32V(segMan, obj, SELECTOR(loop));; - 1);*/
- PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- break;
-
- case SI_RELATIVE_CUE:
- debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received relative cue %d",
- PRINT_REG(obj), cue);
- PUT_SEL32V(segMan, obj, SELECTOR(signal), cue + 0x7f);
- break;
-
- case SI_ABSOLUTE_CUE:
- debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received absolute cue %d",
- PRINT_REG(obj), cue);
- PUT_SEL32V(segMan, obj, SELECTOR(signal), cue);
- break;
-
- case SI_FINISHED:
- debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x finished",
- PRINT_REG(obj));
- PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- PUT_SEL32V(segMan, obj, SELECTOR(state), kSoundStopped);
- break;
-
- default:
- warning("Unexpected result from sfx_poll: %d", result);
- break;
- }
- }
-}
-
-#endif
SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion) :
_resMan(resMan), _segMan(segMan), _kernel(kernel), _audio(audio), _soundVersion(soundVersion) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- // The following hack is needed to ease the change from old to new sound code (because the new sound code does not use SfxState)
- _state = &g_sci->getEngineState()->_sound; // HACK
-#endif
-
- #ifndef USE_OLD_MUSIC_FUNCTIONS
- _music = new SciMusic(_soundVersion);
- _music->init();
- #endif
-
- switch (_soundVersion) {
- case SCI_VERSION_0_EARLY:
- case SCI_VERSION_0_LATE:
- SOUNDCOMMAND(cmdInitSound);
- SOUNDCOMMAND(cmdPlaySound);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdDisposeSound);
- SOUNDCOMMAND(cmdMuteSound);
- SOUNDCOMMAND(cmdStopSound);
- SOUNDCOMMAND(cmdPauseSound);
- SOUNDCOMMAND(cmdResumeSound);
- SOUNDCOMMAND(cmdMasterVolume);
- SOUNDCOMMAND(cmdUpdateSound);
- SOUNDCOMMAND(cmdFadeSound);
- SOUNDCOMMAND(cmdGetPolyphony);
- SOUNDCOMMAND(cmdStopAllSounds);
- _cmdUpdateCuesIndex = -1;
- break;
- case SCI_VERSION_1_EARLY:
- SOUNDCOMMAND(cmdMasterVolume);
- SOUNDCOMMAND(cmdMuteSound);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdGetPolyphony);
- SOUNDCOMMAND(cmdUpdateSound);
- SOUNDCOMMAND(cmdInitSound);
- SOUNDCOMMAND(cmdDisposeSound);
- SOUNDCOMMAND(cmdPlaySound);
- SOUNDCOMMAND(cmdStopSound);
- SOUNDCOMMAND(cmdPauseSound);
- SOUNDCOMMAND(cmdFadeSound);
- SOUNDCOMMAND(cmdUpdateCues);
- SOUNDCOMMAND(cmdSendMidi);
- SOUNDCOMMAND(cmdReverb);
- SOUNDCOMMAND(cmdSetSoundHold);
- _cmdUpdateCuesIndex = 11;
- break;
- case SCI_VERSION_1_LATE:
- SOUNDCOMMAND(cmdMasterVolume);
- SOUNDCOMMAND(cmdMuteSound);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdGetPolyphony);
- SOUNDCOMMAND(cmdGetAudioCapability);
- SOUNDCOMMAND(cmdSuspendSound);
- SOUNDCOMMAND(cmdInitSound);
- SOUNDCOMMAND(cmdDisposeSound);
- SOUNDCOMMAND(cmdPlaySound);
- SOUNDCOMMAND(cmdStopSound);
- SOUNDCOMMAND(cmdPauseSound);
- SOUNDCOMMAND(cmdFadeSound);
- SOUNDCOMMAND(cmdSetSoundHold);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdSetSoundVolume);
- SOUNDCOMMAND(cmdSetSoundPriority);
- SOUNDCOMMAND(cmdSetSoundLoop);
- SOUNDCOMMAND(cmdUpdateCues);
- SOUNDCOMMAND(cmdSendMidi);
- SOUNDCOMMAND(cmdReverb);
- SOUNDCOMMAND(cmdUpdateSound);
- _cmdUpdateCuesIndex = 17;
- break;
- default:
- warning("Sound command parser: unknown sound version %d", _soundVersion);
- break;
- }
+ _music = new SciMusic(_soundVersion);
+ _music->init();
}
SoundCommandParser::~SoundCommandParser() {
- for (SoundCommandContainer::iterator i = _soundCommands.begin(); i != _soundCommands.end(); ++i)
- delete *i;
-
-#ifndef USE_OLD_MUSIC_FUNCTIONS
delete _music;
-#endif
}
-reg_t SoundCommandParser::parseCommand(int argc, reg_t *argv, reg_t acc) {
- uint16 command = argv[0].toUint16();
- reg_t obj = (argc > 1) ? argv[1] : NULL_REG;
- int16 value = (argc > 2) ? argv[2].toSint16() : 0;
- _acc = acc;
- _argc = argc;
- _argv = argv;
-
- if (argc == 6) { // cmdSendMidi
- byte channel = argv[2].toUint16() & 0xf;
- byte midiCmd = argv[3].toUint16() & 0xff;
-
- uint16 controller = argv[4].toUint16();
- uint16 param = argv[5].toUint16();
-
- _midiCommand = (channel | midiCmd) | ((uint32)controller << 8) | ((uint32)param << 16);
- }
-
- if (command < _soundCommands.size()) {
- if (command != _cmdUpdateCuesIndex) {
- //printf("%s, object %04x:%04x\n", _soundCommands[command]->desc, PRINT_REG(obj)); // debug
- debugC(2, kDebugLevelSound, "%s, object %04x:%04x", _soundCommands[command]->desc, PRINT_REG(obj));
- }
-
- (this->*(_soundCommands[command]->sndCmd))(obj, value);
- } else {
- warning("Invalid sound command requested (%d), valid range is 0-%d", command, _soundCommands.size() - 1);
- }
-
- return _acc;
+reg_t SoundCommandParser::kDoSoundInit(int argc, reg_t *argv, reg_t acc) {
+ debugC(2, kDebugLevelSound, "kDoSound(init): %04x:%04x", PRINT_REG(argv[0]));
+ processInitSound(argv[0]);
+ return acc;
}
-void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
- int resourceId = GET_SEL32V(_segMan, obj, SELECTOR(number));
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-
- SongHandle handle = FROBNICATE_HANDLE(obj);
-
- if (_soundVersion != SCI_VERSION_1_LATE) {
- if (!obj.segment)
- return;
- }
-
- SongIteratorType type = (_soundVersion <= SCI_VERSION_0_LATE) ? SCI_SONG_ITERATOR_TYPE_SCI0 : SCI_SONG_ITERATOR_TYPE_SCI1;
-
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr))) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- _state->sfx_remove_song(handle);
- }
- }
-
- if (!obj.segment || !_resMan->testResource(ResourceId(kResourceTypeSound, resourceId)))
- return;
-
- _state->sfx_add_song(build_iterator(_resMan, resourceId, type, handle), 0, handle, resourceId);
-
-
- // Notify the engine
- if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized);
- else
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
-
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
-
-#else
+void SoundCommandParser::processInitSound(reg_t obj) {
+ int resourceId = readSelectorValue(_segMan, obj, SELECTOR(number));
// Check if a track with the same sound object is already playing
MusicEntry *oldSound = _music->getSlot(obj);
if (oldSound)
- cmdDisposeSound(obj, value);
+ processDisposeSound(obj);
MusicEntry *newSound = new MusicEntry();
newSound->resourceId = resourceId;
@@ -302,10 +66,13 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
newSound->soundRes = 0;
newSound->soundObj = obj;
- newSound->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- newSound->priority = GET_SEL32V(_segMan, obj, SELECTOR(pri)) & 0xFF;
- if (_soundVersion >= SCI_VERSION_1_LATE)
- newSound->volume = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX);
+ newSound->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
+ newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(pri)) & 0xFF;
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX);
+
+ debugC(2, kDebugLevelSound, "kDoSound(init): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj),
+ resourceId, newSound->loop, newSound->priority, newSound->volume);
// In SCI1.1 games, sound effects are started from here. If we can find
// a relevant audio resource, play it, otherwise switch to synthesized
@@ -327,492 +94,288 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
if (newSound->soundRes || newSound->pStreamAud) {
// Notify the engine
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized);
else
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
+ writeSelector(_segMan, obj, SELECTOR(handle), obj);
}
-#endif
-
}
-void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
-
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
- _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop)));
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying);
- } else if (_soundVersion == SCI_VERSION_1_EARLY) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
- _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop)));
- _state->sfx_song_renice(handle, GET_SEL32V(_segMan, obj, SELECTOR(pri)));
- RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) value; /* Too lazy to look up a default value for this */
- _state->_songlib.setSongRestoreBehavior(handle, rb);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0);
- } else if (_soundVersion == SCI_VERSION_1_LATE) {
- int looping = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- //int vol = GET_SEL32V(_segMan, obj, SELECTOR(vol));
- int pri = GET_SEL32V(_segMan, obj, SELECTOR(pri));
- int sampleLen = 0;
- Song *song = _state->_songlib.findSong(handle);
- int songNumber = GET_SEL32V(_segMan, obj, SELECTOR(number));
-
- if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- _state->sfx_remove_song(handle);
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
- }
-
- if (!GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && obj.segment) {
- // In SCI1.1 games, sound effects are started from here. If we can find
- // a relevant audio resource, play it, otherwise switch to synthesized
- // effects. If the resource exists, play it using map 65535 (sound
- // effects map)
- if (_resMan->testResource(ResourceId(kResourceTypeAudio, songNumber)) &&
- getSciVersion() >= SCI_VERSION_1_1) {
- // Found a relevant audio resource, play it
- _audio->stopAudio();
- warning("Initializing audio resource instead of requested sound resource %d", songNumber);
- sampleLen = _audio->startAudio(65535, songNumber);
- // Also create iterator, that will fire SI_FINISHED event, when the sound is done playing
- _state->sfx_add_song(new_timer_iterator(sampleLen), 0, handle, songNumber);
- } else {
- if (!_resMan->testResource(ResourceId(kResourceTypeSound, songNumber))) {
- warning("Could not open song number %d", songNumber);
- // Send a "stop handle" event so that the engine won't wait forever here
- _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- return;
- }
- debugC(2, kDebugLevelSound, "Initializing song number %d", songNumber);
- _state->sfx_add_song(build_iterator(_resMan, songNumber, SCI_SONG_ITERATOR_TYPE_SCI1,
- handle), 0, handle, songNumber);
- }
-
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
- }
-
- if (obj.segment) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
- _state->sfx_song_set_loops(handle, looping);
- _state->sfx_song_renice(handle, pri);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0);
- }
- }
-
-#else
+reg_t SoundCommandParser::kDoSoundPlay(int argc, reg_t *argv, reg_t acc) {
+ debugC(2, kDebugLevelSound, "kDoSound(play): %04x:%04x", PRINT_REG(argv[0]));
+ processPlaySound(argv[0]);
+ return acc;
+}
+void SoundCommandParser::processPlaySound(reg_t obj) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdPlaySound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(play): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
- int resourceId = obj.segment ? GET_SEL32V(_segMan, obj, SELECTOR(number)) : -1;
+ int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1;
if (musicSlot->resourceId != resourceId) { // another sound loaded into struct
- cmdDisposeSound(obj, value);
- cmdInitSound(obj, value);
+ processDisposeSound(obj);
+ processInitSound(obj);
// Find slot again :)
musicSlot = _music->getSlot(obj);
}
- int16 loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- debugC(2, kDebugLevelSound, "cmdPlaySound: resource number %d, loop %d", resourceId, loop);
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
+ writeSelector(_segMan, obj, SELECTOR(handle), obj);
if (_soundVersion >= SCI_VERSION_1_EARLY) {
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
- PUT_SEL32V(_segMan, obj, SELECTOR(min), 0);
- PUT_SEL32V(_segMan, obj, SELECTOR(sec), 0);
- PUT_SEL32V(_segMan, obj, SELECTOR(frame), 0);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
+ writeSelectorValue(_segMan, obj, SELECTOR(min), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(sec), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(frame), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), 0);
} else {
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying);
}
- musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- musicSlot->priority = GET_SEL32V(_segMan, obj, SELECTOR(priority));
- if (_soundVersion >= SCI_VERSION_1_LATE)
- musicSlot->volume = GET_SEL32V(_segMan, obj, SELECTOR(vol));
- _music->soundPlay(musicSlot);
+ musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
+ musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority));
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol));
-#endif
+ debugC(2, kDebugLevelSound, "kDoSound(play): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj),
+ resourceId, musicSlot->loop, musicSlot->priority, musicSlot->volume);
+ _music->soundPlay(musicSlot);
}
-void SoundCommandParser::cmdDummy(reg_t obj, int16 value) {
- warning("cmdDummy invoked"); // not supposed to occur
+reg_t SoundCommandParser::kDoSoundRestore(int argc, reg_t *argv, reg_t acc) {
+ // Called after loading, to restore the playlist
+ // We don't really use or need this
+ return acc;
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-void SoundCommandParser::changeSoundStatus(reg_t obj, int newStatus) {
- SongHandle handle = FROBNICATE_HANDLE(obj);
- if (obj.segment) {
- _state->sfx_song_set_status(handle, newStatus);
- if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), newStatus);
- }
+reg_t SoundCommandParser::kDoSoundDummy(int argc, reg_t *argv, reg_t acc) {
+ warning("cmdDummy invoked"); // not supposed to occur
+ return acc;
}
-#endif
-void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- changeSoundStatus(obj, SOUND_STATUS_STOPPED);
-
- if (obj.segment) {
- _state->sfx_remove_song(handle);
-
- if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0x0000);
- }
-
-#else
+reg_t SoundCommandParser::kDoSoundDispose(int argc, reg_t *argv, reg_t acc) {
+ debugC(2, kDebugLevelSound, "kDoSound(dispose): %04x:%04x", PRINT_REG(argv[0]));
+ processDisposeSound(argv[0]);
+ return acc;
+}
+void SoundCommandParser::processDisposeSound(reg_t obj) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdDisposeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(dispose): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
- cmdStopSound(obj, value);
+ processStopSound(obj, false);
_music->soundKill(musicSlot);
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
if (_soundVersion >= SCI_VERSION_1_EARLY)
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
else
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped);
-#endif
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
}
-void SoundCommandParser::cmdStopSound(reg_t obj, int16 value) {
- processStopSound(obj, value, false);
+reg_t SoundCommandParser::kDoSoundStop(int argc, reg_t *argv, reg_t acc) {
+ debugC(2, kDebugLevelSound, "kDoSound(stop): %04x:%04x", PRINT_REG(argv[0]));
+ processStopSound(argv[0], false);
+ return acc;
}
-void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFinishedPlaying) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- changeSoundStatus(obj, SOUND_STATUS_STOPPED);
-
- if (_soundVersion >= SCI_VERSION_1_EARLY)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
-#else
+void SoundCommandParser::processStopSound(reg_t obj, bool sampleFinishedPlaying) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdStopSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(stop): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
if (_soundVersion <= SCI_VERSION_0_LATE) {
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
} else {
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
}
- // Set signal selector in sound SCI0 games only, when the sample has finished playing
- // If we don't set it at all, we get a problem when using vaporizer on the 2 guys
- // If we set it all the time, we get no music in sq3new and kq1
- // FIXME: this *may* be wrong, it's impossible to find out in sierra DOS sci, because SCI0 under DOS didn't have
- // sfx drivers included
- // We need to set signal in sound SCI1+ games all the time
+ // Set signal selector in sound SCI0 games only, when the sample has
+ // finished playing. If we don't set it at all, we get a problem when using
+ // vaporizer on the 2 guys. If we set it all the time, we get no music in
+ // sq3new and kq1.
+ // FIXME: This *may* be wrong, it's impossible to find out in Sierra DOS
+ // SCI, because SCI0 under DOS didn't have sfx drivers included.
+ // We need to set signal in sound SCI1+ games all the time.
if ((_soundVersion > SCI_VERSION_0_LATE) || sampleFinishedPlaying)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
musicSlot->dataInc = 0;
musicSlot->signal = 0;
_music->soundStop(musicSlot);
-#endif
}
-void SoundCommandParser::cmdPauseSound(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (!obj.segment)
- return;
-
- if (_soundVersion <= SCI_VERSION_0_LATE)
- changeSoundStatus(obj, SOUND_STATUS_SUSPENDED);
+reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) {
+ if (argc == 1)
+ debugC(2, kDebugLevelSound, "kDoSound(pause): %04x:%04x", PRINT_REG(argv[0]));
else
- changeSoundStatus(obj, value ? SOUND_STATUS_SUSPENDED : SOUND_STATUS_PLAYING);
-#else
+ debugC(2, kDebugLevelSound, "kDoSound(pause): %04x:%04x, %04x:%04x", PRINT_REG(argv[0]), PRINT_REG(argv[1]));
- if (!obj.segment) { // pause the whole playlist
- // Pausing/Resuming the whole playlist was introduced
- // in the SCI1 late sound scheme
- if (_soundVersion <= SCI_VERSION_1_EARLY)
- return;
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ // SCI0 games give us 0/1 for either resuming or pausing the current music
+ // this one doesn't count, so pausing 2 times and resuming once means here that we are supposed to resume
+ uint16 value = argv[0].toUint16();
+ MusicEntry *musicSlot = _music->getActiveSci0MusicSlot();
+ switch (value) {
+ case 1:
+ if ((musicSlot) && (musicSlot->status == kSoundPlaying)) {
+ _music->soundPause(musicSlot);
+ writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused);
+ }
+ return make_reg(0, 0);
+ case 0:
+ if ((musicSlot) && (musicSlot->status == kSoundPaused)) {
+ _music->soundResume(musicSlot);
+ writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
+ return make_reg(0, 1);
+ }
+ return make_reg(0, 0);
+ default:
+ error("kDoSound(pause): parameter 0 is invalid for sound-sci0");
+ }
+ }
+ reg_t obj = argv[0];
+ uint16 value = argc > 1 ? argv[1].toUint16() : 0;
+ if (!obj.segment) { // pause the whole playlist
_music->pauseAll(value);
} else { // pause a playlist slot
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdPauseSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ // This happens quite frequently
+ debugC(2, kDebugLevelSound, "kDoSound(pause): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- // Always pause the sound in SCI0 games. It's resumed in cmdResumeSound()
- PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused);
- _music->soundPause(musicSlot);
- } else {
- _music->soundToggle(musicSlot, value);
- }
+ _music->soundToggle(musicSlot, value);
}
-
-#endif
+ return acc;
}
-void SoundCommandParser::cmdResumeSound(reg_t obj, int16 value) {
- // SCI0 only command
-
- if (!obj.segment)
- return;
+// SCI0 only command
+// It's called right after restoring a game - it's responsible to kick off playing music again
+// we don't need this at all, so we don't do anything here
+reg_t SoundCommandParser::kDoSoundResumeAfterRestore(int argc, reg_t *argv, reg_t acc) {
+ return acc;
+}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- changeSoundStatus(obj, SOUND_STATUS_PLAYING);
-#else
- MusicEntry *musicSlot = _music->getSlot(obj);
- if (!musicSlot) {
- warning("cmdResumeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+reg_t SoundCommandParser::kDoSoundMute(int argc, reg_t *argv, reg_t acc) {
+ uint16 previousState = _music->soundGetSoundOn();
+ if (argc > 0) {
+ debugC(2, kDebugLevelSound, "kDoSound(mute): %d", argv[0].toUint16());
+ _music->soundSetSoundOn(argv[0].toUint16());
}
- PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
- _music->soundResume(musicSlot);
-#endif
+ return make_reg(0, previousState);
}
-void SoundCommandParser::cmdMuteSound(reg_t obj, int16 value) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- if (_argc > 1) // the first parameter is the sound command
- _music->soundSetSoundOn(obj.toUint16());
- _acc = make_reg(0, _music->soundGetSoundOn());
-#endif
-}
-
-void SoundCommandParser::cmdMasterVolume(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- _acc = make_reg(0, _state->sfx_getVolume());
-
- if (obj != SIGNAL_REG)
- _state->sfx_setVolume(obj.toSint16());
-#else
- debugC(2, kDebugLevelSound, "cmdMasterVolume: %d", value);
- _acc = make_reg(0, _music->soundGetMasterVolume());
+reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc) {
+ acc = make_reg(0, _music->soundGetMasterVolume());
- if (_argc > 1) { // the first parameter is the sound command
- int vol = CLIP<int16>(obj.toSint16(), 0, kMaxSciVolume);
+ if (argc > 0) {
+ debugC(2, kDebugLevelSound, "kDoSound(masterVolume): %d", argv[0].toSint16());
+ int vol = CLIP<int16>(argv[0].toSint16(), 0, kMaxSciVolume);
vol = vol * Audio::Mixer::kMaxMixerVolume / kMaxSciVolume;
ConfMan.setInt("music_volume", vol);
ConfMan.setInt("sfx_volume", vol);
g_engine->syncSoundSettings();
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- if (_soundVersion != SCI_VERSION_1_LATE) {
- /*s->sound_server->command(s, SOUND_COMMAND_FADE_HANDLE, obj, 120);*/ /* Fade out in 2 secs */
- /* FIXME: The next couple of lines actually STOP the handle, rather
- ** than fading it! */
- _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- } else {
- fade_params_t fade;
- fade.final_volume = _argv[2].toUint16();
- fade.ticks_per_step = _argv[3].toUint16();
- fade.step_size = _argv[4].toUint16();
- fade.action = _argv[5].toUint16() ?
- FADE_ACTION_FADE_AND_STOP :
- FADE_ACTION_FADE_AND_CONT;
-
- _state->sfx_song_set_fade(handle, &fade);
-
- /* FIXME: The next couple of lines actually STOP the handle, rather
- ** than fading it! */
- if (_argv[5].toUint16()) {
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- } else {
- // FIXME: Support fade-and-continue. For now, send signal right away.
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- }
- }
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdFadeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
int volume = musicSlot->volume;
- switch (_argc) {
- case 2: // SCI0
- // SCI0 fades out all the time and when fadeout is done it will also stop the music from playing
+ // If sound is not playing currently, set signal directly
+ if (musicSlot->status != kSoundPlaying) {
+ debugC(2, kDebugLevelSound, "kDoSound(fade): %04x:%04x fading requested, but sound is currently not playing", PRINT_REG(obj));
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ return acc;
+ }
+
+ switch (argc) {
+ case 1: // SCI0
+ // SCI0 fades out all the time and when fadeout is done it will also
+ // stop the music from playing
musicSlot->fadeTo = 0;
musicSlot->fadeStep = -5;
musicSlot->fadeTickerStep = 10 * 16667 / _music->soundGetTempo();
musicSlot->fadeTicker = 0;
break;
- case 5: // SCI01+
- case 6: // SCI1+ (SCI1 late sound scheme), with fade and continue
- musicSlot->fadeTo = CLIP<uint16>(_argv[2].toUint16(), 0, MUSIC_VOLUME_MAX);
- musicSlot->fadeStep = volume > _argv[2].toUint16() ? -_argv[4].toUint16() : _argv[4].toUint16();
- musicSlot->fadeTickerStep = _argv[3].toUint16() * 16667 / _music->soundGetTempo();
+ case 4: // SCI01+
+ case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue
+ musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX);
+ // sometimes we get objects in that position, fix it up (ffs. workarounds)
+ if (!argv[1].segment)
+ musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16();
+ else
+ musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5;
+ musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo();
musicSlot->fadeTicker = 0;
- musicSlot->stopAfterFading = (_argc == 6) ? (_argv[5].toUint16() != 0) : false;
+ musicSlot->stopAfterFading = (argc == 5) ? (argv[4].toUint16() != 0) : false;
break;
default:
- error("cmdFadeSound: unsupported argc %d", _argc);
- }
-
- // If sound is not playing currently, set signal directly
- if (musicSlot->status != kSoundPlaying) {
- warning("cmdFadeSound: fading requested, but sound is currently not playing");
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ error("kDoSound(fade): unsupported argc %d", argc);
}
- debugC(2, kDebugLevelSound, "cmdFadeSound: to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
-#endif
+ debugC(2, kDebugLevelSound, "kDoSound(fade): %04x:%04x to %d, step %d, ticker %d", PRINT_REG(obj), musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
+ return acc;
}
-void SoundCommandParser::cmdGetPolyphony(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- _acc = make_reg(0, _state->sfx_get_player_polyphony());
-#else
- _acc = make_reg(0, _music->soundGetVoices()); // Get the number of voices
-#endif
+reg_t SoundCommandParser::kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc) {
+ return make_reg(0, _music->soundGetVoices()); // Get the number of voices
}
-void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundUpdate(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+
+ debugC(2, kDebugLevelSound, "kDoSound(update): %04x:%04x", PRINT_REG(argv[0]));
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- if (_soundVersion <= SCI_VERSION_0_LATE && obj.segment) {
- _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop)));
- script_set_priority(_resMan, _segMan, _state, obj, GET_SEL32V(_segMan, obj, SELECTOR(pri)));
- }
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdUpdateSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(update): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
- musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- int16 objVol = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, 255);
+ musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
+ int16 objVol = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, 255);
if (objVol != musicSlot->volume)
_music->soundSetVolume(musicSlot, objVol);
- uint32 objPrio = GET_SEL32V(_segMan, obj, SELECTOR(pri));
+ uint32 objPrio = readSelectorValue(_segMan, obj, SELECTOR(pri));
if (objPrio != musicSlot->priority)
_music->soundSetPriority(musicSlot, objPrio);
-
-#endif
+ return acc;
}
-void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- int signal = 0;
- int min = 0;
- int sec = 0;
- int frame = 0;
- int result = SI_LOOP; // small hack
- SongHandle handle = FROBNICATE_HANDLE(obj);
-
- while (result == SI_LOOP)
- result = _state->sfx_poll_specific(handle, &signal);
-
- switch (result) {
- case SI_ABSOLUTE_CUE:
- debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Absolute Cue: %d",
- PRINT_REG(obj), signal);
- debugC(2, kDebugLevelSound, "abs-signal %04X", signal);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal);
- break;
-
- case SI_RELATIVE_CUE:
- debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Relative Cue: %d",
- PRINT_REG(obj), signal);
-
- /* FIXME to match commented-out semantics
- * below, with proper storage of dataInc and
- * signal in the iterator code. */
- PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), signal);
- debugC(2, kDebugLevelSound, "rel-signal %04X", signal);
- if (_soundVersion == SCI_VERSION_1_EARLY)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal);
- else
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal + 127);
- break;
-
- case SI_FINISHED:
- debugC(2, kDebugLevelSound, "--- [FINISHED] %04x:%04x", PRINT_REG(obj));
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- break;
-
- case SI_LOOP:
- break; // Doesn't happen
- }
+reg_t SoundCommandParser::kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc) {
+ processUpdateCues(argv[0]);
+ return acc;
+}
- //switch (signal) {
- //case 0x00:
- // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) {
- // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc);
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc+0x7f);
- // } else {
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal);
- // }
- // break;
- //case 0xFF: // May be unnecessary
- // s->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- // break;
- //default :
- // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) {
- // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc);
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc + 0x7f);
- // } else {
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal);
- // }
- // break;
- //}
-
- if (_soundVersion == SCI_VERSION_1_EARLY) {
- PUT_SEL32V(_segMan, obj, SELECTOR(min), min);
- PUT_SEL32V(_segMan, obj, SELECTOR(sec), sec);
- PUT_SEL32V(_segMan, obj, SELECTOR(frame), frame);
- }
-#else
+void SoundCommandParser::processUpdateCues(reg_t obj) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdUpdateCues: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(updateCues): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
@@ -828,9 +391,13 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
musicSlot->loop -= currentLoopCounter - musicSlot->sampleLoopCounter;
musicSlot->sampleLoopCounter = currentLoopCounter;
}
- if (!_music->soundIsActive(musicSlot)) {
- processStopSound(obj, 0, true);
- } else {
+ if (musicSlot->status == kSoundPlaying) {
+ if (!_music->soundIsActive(musicSlot)) {
+ processStopSound(obj, true);
+ } else {
+ _music->updateAudioStreamTicker(musicSlot);
+ }
+ } else if (musicSlot->status == kSoundPaused) {
_music->updateAudioStreamTicker(musicSlot);
}
// We get a flag from MusicEntry::doFade() here to set volume for the stream
@@ -841,150 +408,169 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
} else if (musicSlot->pMidiParser) {
// Update MIDI slots
if (musicSlot->signal == 0) {
- if (musicSlot->dataInc != GET_SEL32V(_segMan, obj, SELECTOR(dataInc))) {
- if (_kernel->_selectorCache.dataInc > -1)
- PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127);
+ if (musicSlot->dataInc != readSelectorValue(_segMan, obj, SELECTOR(dataInc))) {
+ if (SELECTOR(dataInc) > -1)
+ writeSelectorValue(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127);
}
} else {
// Sync the signal of the sound object
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->signal);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->signal);
// We need to do this especially because state selector needs to get updated
if (musicSlot->signal == SIGNAL_OFFSET)
- cmdStopSound(obj, 0);
+ processStopSound(obj, false);
}
} else {
- // Slot actually has no data (which would mean that a sound-resource w/ unsupported data is used
+ // Slot actually has no data (which would mean that a sound-resource w/
+ // unsupported data is used.
// (example lsl5 - sound resource 744 - it's roland exclusive
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- // If we don't set signal here, at least the switch to the mud wrestling room in lsl5 will not work
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ // If we don't set signal here, at least the switch to the mud wrestling
+ // room in lsl5 will not work.
}
if (musicSlot->fadeCompleted) {
musicSlot->fadeCompleted = false;
// We need signal for sci0 at least in iceman as well (room 14, fireworks)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
if (_soundVersion <= SCI_VERSION_0_LATE) {
- cmdStopSound(obj, 0);
+ processStopSound(obj, false);
} else {
if (musicSlot->stopAfterFading)
- cmdStopSound(obj, 0);
+ processStopSound(obj, false);
}
}
// Sync loop selector for SCI0
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop);
+ writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop);
musicSlot->signal = 0;
if (_soundVersion >= SCI_VERSION_1_EARLY) {
- PUT_SEL32V(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600);
- PUT_SEL32V(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60);
- PUT_SEL32V(_segMan, obj, SELECTOR(frame), musicSlot->ticker);
+ writeSelectorValue(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600);
+ writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60);
+ writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker);
}
-
-#endif
}
-void SoundCommandParser::cmdSendMidi(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- //SongHandle handle = FROBNICATE_HANDLE(obj);
- //_state->sfx_send_midi(handle, value, _midiCmd, _controller, _param);
-#else
- _music->sendMidiCommand(_midiCommand);
-#endif
+reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ byte channel = argv[1].toUint16() & 0xf;
+ byte midiCmd = argv[2].toUint16() & 0xff;
+
+ // TODO: first there is a 4-parameter variant of this call which needs to get reversed
+ // second the current code isn't 100% accurate, sierra sci does checks on the 4th parameter
+ if (argc == 4)
+ return acc;
+
+ uint16 controller = argv[3].toUint16();
+ uint16 param = argv[4].toUint16();
+
+ debugC(2, kDebugLevelSound, "kDoSound(sendMidi): %04x:%04x, %d, %d, %d, %d", PRINT_REG(obj), channel, midiCmd, controller, param);
+ if (channel)
+ channel--; // channel is given 1-based, we are using 0-based
+
+ uint32 midiCommand = (channel | midiCmd) | ((uint32)controller << 8) | ((uint32)param << 16);
+
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ // TODO: maybe it's possible to call this with obj == 0:0 and send directly?!
+ // if so, allow it
+ //_music->sendMidiCommand(_midiCommand);
+ warning("kDoSound(sendMidi): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
+ }
+ _music->sendMidiCommand(musicSlot, midiCommand);
+ return acc;
}
-void SoundCommandParser::cmdReverb(reg_t obj, int16 value) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- _music->setReverb(obj.toUint16() & 0xF);
-#endif
+reg_t SoundCommandParser::kDoSoundReverb(int argc, reg_t *argv, reg_t acc) {
+ debugC(2, kDebugLevelSound, "doSoundReverb: %d", argv[0].toUint16() & 0xF);
+ _music->setReverb(argv[0].toUint16() & 0xF);
+ return acc;
}
-void SoundCommandParser::cmdSetSoundHold(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- _state->sfx_song_set_hold(handle, value);
-#else
+reg_t SoundCommandParser::kDoSoundSetHold(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+
+ debugC(2, kDebugLevelSound, "doSoundSetHold: %04x:%04x, %d", PRINT_REG(argv[0]), argv[1].toUint16());
+
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdSetSoundHold: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(setHold): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
// Set the special hold marker ID where the song should be looped at.
- musicSlot->hold = value;
-#endif
+ musicSlot->hold = argv[1].toSint16();
+ return acc;
}
-void SoundCommandParser::cmdGetAudioCapability(reg_t obj, int16 value) {
+reg_t SoundCommandParser::kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc) {
// Tests for digital audio support
- _acc = make_reg(0, 1);
+ return make_reg(0, 1);
}
-void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- Common::StackLock(_music->_mutex);
+reg_t SoundCommandParser::kDoSoundStopAll(int argc, reg_t *argv, reg_t acc) {
+ // TODO: this can't be right, this gets called in kq1 - e.g. being in witch house, getting the note
+ // now the point jingle plays and after a messagebox they call this - and would stop the background effects with it
+ // this doesn't make sense, so i disable it for now
+ return acc;
- // FIXME: this can't be right, it's called in iceman (room 14) when the door sound has done playing
- // stopping sounds can't be right, because music is starting afterwards in ssci. can't be resume queued
- // song(s), because music is playing even when this call is nuked inside ssci.
- return;
+ Common::StackLock(_music->_mutex);
const MusicList::iterator end = _music->getPlayListEnd();
for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
if (_soundVersion <= SCI_VERSION_0_LATE) {
- PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped);
} else {
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0);
- PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(handle), 0);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET);
}
(*i)->dataInc = 0;
_music->soundStop(*i);
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ int16 value = argv[1].toSint16();
-#ifndef USE_OLD_MUSIC_FUNCTIONS
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
// Do not throw a warning if the sound can't be found, as in some games
- // this is called before the actual sound is loaded (e.g. SQ4CD, with the
- // drum sounds of the energizer bunny at the beginning), so this is normal
- // behavior
+ // this is called before the actual sound is loaded (e.g. SQ4CD, with
+ // the drum sounds of the energizer bunny at the beginning), so this is
+ // normal behavior.
//warning("cmdSetSoundVolume: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ return acc;
}
- debugC(2, kDebugLevelSound, "cmdSetSoundVolume: %d", value);
+ debugC(2, kDebugLevelSound, "kDoSound(setVolume): %d", value);
value = CLIP<int>(value, 0, MUSIC_VOLUME_MAX);
if (musicSlot->volume != value) {
musicSlot->volume = value;
_music->soundSetVolume(musicSlot, value);
- PUT_SEL32V(_segMan, obj, SELECTOR(vol), value);
+ writeSelectorValue(_segMan, obj, SELECTOR(vol), value);
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ int16 value = argv[1].toSint16();
+
+ debugC(2, kDebugLevelSound, "kDoSound(setPriority): %04x:%04x, %d", PRINT_REG(obj), value);
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- script_set_priority(_resMan, _segMan, _state, obj, value);
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdSetSoundPriority: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ debugC(2, kDebugLevelSound, "kDoSound(setPriority): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
if (value == -1) {
@@ -993,43 +579,39 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) {
if (song->data[0] == 0xf0)
_music->soundSetPriority(musicSlot, song->data[1]);
else
- warning("cmdSetSoundPriority: Attempt to unset song priority when there is no built-in value");
+ warning("kDoSound(setPriority): Attempt to unset song priority when there is no built-in value");
//pSnd->prio=0;field_15B=0
- PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) & 0xFD);
+ writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD);
} else {
// Scripted priority
//pSnd->field_15B=1;
- PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) | 2);
+ writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2);
//DoSOund(0xF,hobj,w)
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ int16 value = argv[1].toSint16();
+
+ debugC(2, kDebugLevelSound, "kDoSound(setLoop): %04x:%04x, %d", PRINT_REG(obj), value);
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (!GET_SEL32(_segMan, obj, SELECTOR(nodePtr)).isNull()) {
- SongHandle handle = FROBNICATE_HANDLE(obj);
- _state->sfx_song_set_loops(handle, value);
- }
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
// Apparently, it's perfectly normal for a game to call cmdSetSoundLoop
// before actually initializing the sound and adding it to the playlist
// with cmdInitSound. Usually, it doesn't matter if the game doesn't
// request to loop the sound, so in this case, don't throw any warning,
- // otherwise do, because the sound won't be looped
+ // otherwise do, because the sound won't be looped.
if (value == -1) {
- warning("cmdSetSoundLoop: Slot not found (%04x:%04x) and the song was requested to be looped", PRINT_REG(obj));
+ warning("kDoSound(setLoop): Slot not found (%04x:%04x) and the song was requested to be looped", PRINT_REG(obj));
} else {
// Doesn't really matter
}
- return;
+ return acc;
}
if (value == -1) {
musicSlot->loop = 0xFFFF;
@@ -1037,38 +619,38 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) {
musicSlot->loop = 1; // actually plays the music once
}
- PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop);
-#endif
+ writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop);
+ return acc;
}
-void SoundCommandParser::cmdSuspendSound(reg_t obj, int16 value) {
+reg_t SoundCommandParser::kDoSoundSuspend(int argc, reg_t *argv, reg_t acc) {
// TODO
- warning("STUB: cmdSuspendSound");
+ warning("kDoSound(suspend): STUB");
+ return acc;
}
-#ifndef USE_OLD_MUSIC_FUNCTIONS
-
void SoundCommandParser::updateSci0Cues() {
bool noOnePlaying = true;
MusicEntry *pWaitingForPlay = NULL;
- _music->_mutex.lock();
-
const MusicList::iterator end = _music->getPlayListEnd();
for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
// Is the sound stopped, and the sound object updated too? If yes, skip
- // this sound, as SCI0 only allows one active song
- if (((*i)->isQueued) && (!pWaitingForPlay)) {
+ // this sound, as SCI0 only allows one active song.
+ if ((*i)->isQueued) {
pWaitingForPlay = (*i);
+ // FIXME(?): In iceman 2 songs are queued when playing the door
+ // sound - if we use the first song for resuming then it's the wrong
+ // one. Both songs have same priority. Maybe the new sound function
+ // in sci0 is somehow responsible.
continue;
}
if ((*i)->signal == 0 && (*i)->status != kSoundPlaying)
continue;
- cmdUpdateCues((*i)->soundObj, 0);
+ processUpdateCues((*i)->soundObj);
noOnePlaying = false;
}
- _music->_mutex.unlock();
if (noOnePlaying && pWaitingForPlay) {
// If there is a queued entry, play it now ffs: SciMusic::soundPlay()
@@ -1077,90 +659,40 @@ void SoundCommandParser::updateSci0Cues() {
}
}
-#endif
-
void SoundCommandParser::clearPlayList() {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->clearPlayList();
-#endif
-}
-
-void SoundCommandParser::syncPlayList(Common::Serializer &s) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- _music->saveLoadWithSerializer(s);
-#endif
-}
-
-void SoundCommandParser::reconstructPlayList(int savegame_version) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- Common::StackLock lock(_music->_mutex);
-
- const MusicList::iterator end = _music->getPlayListEnd();
- for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
- if ((*i)->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resourceId))) {
- (*i)->soundRes = new SoundResource((*i)->resourceId, _resMan, _soundVersion);
- _music->soundInitSnd(*i);
- } else {
- (*i)->soundRes = 0;
- }
- if ((*i)->status == kSoundPlaying) {
- if (savegame_version < 14) {
- (*i)->dataInc = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(dataInc));
- (*i)->signal = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal));
-
- if (_soundVersion >= SCI_VERSION_1_LATE)
- (*i)->volume = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(vol));
- }
-
- cmdPlaySound((*i)->soundObj, 0);
- }
- }
-
-#endif
}
void SoundCommandParser::printPlayList(Console *con) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->printPlayList(con);
-#endif
}
void SoundCommandParser::printSongInfo(reg_t obj, Console *con) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->printSongInfo(obj, con);
-#endif
}
void SoundCommandParser::stopAllSounds() {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->stopAll();
-#endif
}
void SoundCommandParser::startNewSound(int number) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
Common::StackLock lock(_music->_mutex);
// Overwrite the first sound in the playlist
MusicEntry *song = *_music->getPlayListStart();
reg_t soundObj = song->soundObj;
- cmdDisposeSound(soundObj, 0);
- PUT_SEL32V(_segMan, soundObj, SELECTOR(number), number);
- cmdInitSound(soundObj, 0);
- cmdPlaySound(soundObj, 0);
-#endif
+ processDisposeSound(soundObj);
+ writeSelectorValue(_segMan, soundObj, SELECTOR(number), number);
+ processInitSound(soundObj);
+ processPlaySound(soundObj);
}
void SoundCommandParser::setMasterVolume(int vol) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->soundSetMasterVolume(vol);
-#endif
}
void SoundCommandParser::pauseAll(bool pause) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->pauseAll(pause);
-#endif
}
} // End of namespace Sci
diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h
index 09cca23450..8e6fb81762 100644
--- a/engines/sci/sound/soundcmd.h
+++ b/engines/sci/sound/soundcmd.h
@@ -26,8 +26,6 @@
#ifndef SCI_SOUNDCMD_H
#define SCI_SOUNDCMD_H
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
#include "common/list.h"
#include "sci/engine/state.h"
@@ -36,13 +34,13 @@ namespace Sci {
class Console;
class SciMusic;
class SoundCommandParser;
-typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, int16 value);
+//typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, int16 value);
-struct MusicEntryCommand {
- MusicEntryCommand(const char *d, SoundCommand c) : sndCmd(c), desc(d) {}
- SoundCommand sndCmd;
- const char *desc;
-};
+//struct MusicEntryCommand {
+// MusicEntryCommand(const char *d, SoundCommand c) : sndCmd(c), desc(d) {}
+// SoundCommand sndCmd;
+// const char *desc;
+//};
class SoundCommandParser {
public:
@@ -53,11 +51,7 @@ public:
kMaxSciVolume = 15
};
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- void updateSfxState(SfxState *newState) { _state = newState; }
-#endif
-
- reg_t parseCommand(int argc, reg_t *argv, reg_t acc);
+ //reg_t parseCommand(int argc, reg_t *argv, reg_t acc);
// Functions used for game state loading
void clearPlayList();
@@ -69,14 +63,14 @@ public:
void pauseAll(bool pause);
// Debug console functions
- void playSound(reg_t obj) { cmdPlaySound(obj, 0); }
- void stopSound(reg_t obj) { cmdStopSound(obj, 0); }
void startNewSound(int number);
void stopAllSounds();
void printPlayList(Console *con);
void printSongInfo(reg_t obj, Console *con);
-#ifndef USE_OLD_MUSIC_FUNCTIONS
+ void processPlaySound(reg_t obj);
+ void processStopSound(reg_t obj, bool sampleFinishedPlaying);
+
/**
* Synchronizes the current state of the music list to the rest of the engine, so that
* the changes that the sound thread makes to the music are registered with the engine
@@ -85,56 +79,44 @@ public:
* by the engine scripts themselves, so the engine itself polls for changes to the music
*/
void updateSci0Cues();
-#endif
+
+ reg_t kDoSoundInit(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundPlay(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundRestore(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundMute(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundPause(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundResumeAfterRestore(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundStop(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundStopAll(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundDispose(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundFade(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundUpdate(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundReverb(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetHold(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundDummy(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSuspend(int argc, reg_t *argv, reg_t acc);
private:
- typedef Common::Array<MusicEntryCommand *> SoundCommandContainer;
- SoundCommandContainer _soundCommands;
+ //typedef Common::Array<MusicEntryCommand *> SoundCommandContainer;
+ //SoundCommandContainer _soundCommands;
ResourceManager *_resMan;
SegManager *_segMan;
Kernel *_kernel;
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SfxState *_state;
- int _midiCmd, _controller, _param;
-#else
SciMusic *_music;
-#endif
AudioPlayer *_audio;
SciVersion _soundVersion;
- int _argc;
- reg_t *_argv; // for cmdFadeSound
- uint32 _midiCommand; // for cmdSendMidi
- reg_t _acc;
- int _cmdUpdateCuesIndex;
-
- void cmdInitSound(reg_t obj, int16 value);
- void cmdPlaySound(reg_t obj, int16 value);
- void cmdDummy(reg_t obj, int16 value);
- void cmdMuteSound(reg_t obj, int16 value);
- void cmdPauseSound(reg_t obj, int16 value);
- void cmdResumeSound(reg_t obj, int16 value);
- void cmdStopSound(reg_t obj, int16 value);
- void cmdDisposeSound(reg_t obj, int16 value);
- void cmdMasterVolume(reg_t obj, int16 value);
- void cmdFadeSound(reg_t obj, int16 value);
- void cmdGetPolyphony(reg_t obj, int16 value);
- void cmdStopAllSounds(reg_t obj, int16 value);
- void cmdUpdateSound(reg_t obj, int16 value);
- void cmdUpdateCues(reg_t obj, int16 value);
- void cmdSendMidi(reg_t obj, int16 value);
- void cmdReverb(reg_t obj, int16 value);
- void cmdSetSoundHold(reg_t obj, int16 value);
- void cmdGetAudioCapability(reg_t obj, int16 value);
- void cmdSetSoundVolume(reg_t obj, int16 value);
- void cmdSetSoundPriority(reg_t obj, int16 value);
- void cmdSetSoundLoop(reg_t obj, int16 value);
- void cmdSuspendSound(reg_t obj, int16 value);
-
- void processStopSound(reg_t obj, int16 value, bool sampleFinishedPlaying);
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- void changeSoundStatus(reg_t obj, int newStatus);
-#endif
+
+ void processInitSound(reg_t obj);
+ void processDisposeSound(reg_t obj);
+ void processUpdateCues(reg_t obj);
};
} // End of namespace Sci
diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp
index 2c117ae329..58fd60621d 100644
--- a/engines/sci/video/seq_decoder.cpp
+++ b/engines/sci/video/seq_decoder.cpp
@@ -55,10 +55,10 @@ SeqDecoder::~SeqDecoder() {
close();
}
-bool SeqDecoder::load(Common::SeekableReadStream &stream) {
+bool SeqDecoder::load(Common::SeekableReadStream *stream) {
close();
- _fileStream = &stream;
+ _fileStream = stream;
_surface = new Graphics::Surface();
_surface->create(SEQ_SCREEN_WIDTH, SEQ_SCREEN_HEIGHT, 1);
@@ -76,6 +76,7 @@ bool SeqDecoder::load(Common::SeekableReadStream &stream) {
uint16 palColorCount = READ_LE_UINT16(paletteData + 29);
int palOffset = 37;
+ memset(_palette, 0, 256 * 3);
for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
if (palFormat == kSeqPalVariable)
diff --git a/engines/sci/video/seq_decoder.h b/engines/sci/video/seq_decoder.h
index 416abb78fa..1714477083 100644
--- a/engines/sci/video/seq_decoder.h
+++ b/engines/sci/video/seq_decoder.h
@@ -38,7 +38,7 @@ public:
SeqDecoder();
virtual ~SeqDecoder();
- bool load(Common::SeekableReadStream &stream);
+ bool load(Common::SeekableReadStream *stream);
void close();
void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; }
diff --git a/engines/sci/video/vmd_decoder.cpp b/engines/sci/video/vmd_decoder.cpp
deleted file mode 100644
index 93132bc5d6..0000000000
--- a/engines/sci/video/vmd_decoder.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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 ENABLE_SCI32
-
-#include "sci/video/vmd_decoder.h"
-
-#include "common/endian.h"
-#include "common/util.h"
-#include "common/stream.h"
-#include "common/system.h"
-
-#include "graphics/dither.h"
-
-#include "sound/mixer.h"
-#include "sound/audiostream.h"
-
-namespace Sci {
-
-VMDDecoder::VMDDecoder(Audio::Mixer *mixer) : _mixer(mixer) {
- _vmdDecoder = new Graphics::Vmd(new Graphics::PaletteLUT(5, Graphics::PaletteLUT::kPaletteYUV));
- _surface = 0;
- _dirtyPalette = false;
- _fileStream = 0;
-}
-
-VMDDecoder::~VMDDecoder() {
- close();
-}
-
-bool VMDDecoder::load(Common::SeekableReadStream &stream) {
- close();
-
- if (!_vmdDecoder->load(stream))
- return false;
-
- _fileStream = &stream;
-
- if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesPalette)
- loadPaletteFromVMD();
-
- if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesSound)
- _vmdDecoder->enableSound(*_mixer);
-
- if (_vmdDecoder->hasExtraData())
- warning("This VMD video has extra embedded data, which is currently not handled");
-
- _surface = new Graphics::Surface();
- _surface->create(_vmdDecoder->getWidth(), _vmdDecoder->getHeight(), 1);
- _vmdDecoder->setVideoMemory((byte *)_surface->pixels, _surface->w, _surface->h);
- return true;
-}
-
-void VMDDecoder::close() {
- if (!_fileStream)
- return;
-
- _vmdDecoder->unload();
-
- delete _fileStream;
- _fileStream = 0;
-
- _surface->free();
- delete _surface;
- _surface = 0;
-
- reset();
-}
-
-Graphics::Surface *VMDDecoder::decodeNextFrame() {
- Graphics::CoktelVideo::State state = _vmdDecoder->nextFrame();
-
- if (state.flags & Graphics::CoktelVideo::kStatePalette)
- loadPaletteFromVMD();
-
- if (_curFrame == -1)
- _startTime = g_system->getMillis();
-
- _curFrame++;
- return _surface;
-}
-
-void VMDDecoder::loadPaletteFromVMD() {
- const byte *pal = _vmdDecoder->getPalette();
-
- for (int i = 0; i < 256; i++) {
- _palette[i * 3 + 0] = pal[i * 3 + 0] << 2;
- _palette[i * 3 + 1] = pal[i * 3 + 1] << 2;
- _palette[i * 3 + 2] = pal[i * 3 + 2] << 2;
- }
-
- _dirtyPalette = true;
-}
-
-} // End of namespace Graphics
-
-#endif
diff --git a/engines/sci/video/vmd_decoder.h b/engines/sci/video/vmd_decoder.h
deleted file mode 100644
index 231da9202e..0000000000
--- a/engines/sci/video/vmd_decoder.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along 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 ENABLE_SCI32
-
-#ifndef GRAPHICS_VIDEO_VMD_DECODER_H
-#define GRAPHICS_VIDEO_VMD_DECODER_H
-
-#include "graphics/video/coktelvideo/coktelvideo.h"
-#include "graphics/video/video_decoder.h"
-#include "sound/mixer.h"
-
-namespace Sci {
-
-/**
- * Wrapper for the Coktel Vision VMD video decoder
- * for videos by Coktel Vision/Sierra.
- *
- * VMD videos were used in the following SCI21/SCI3
- * adventure games, developed by Sierra:
- * - Gabriel Knight 2: The Beast Within
- * - Leisure Suit Larry 7
- * - Lighthouse
- * - Phantasmagoria 1
- * - RAMA
- * - Shivers
- * - Shivers 2: Harvest of Souls
- * - Torin's Passage
- */
-class VMDDecoder : public Graphics::FixedRateVideoDecoder {
-public:
- VMDDecoder(Audio::Mixer *mixer);
- virtual ~VMDDecoder();
-
- uint32 getFrameWaitTime();
-
- bool load(Common::SeekableReadStream &stream);
- void close();
-
- bool isVideoLoaded() const { return _fileStream != 0; }
- uint16 getWidth() const { return _surface->w; }
- uint16 getHeight() const { return _surface->h; }
- uint32 getFrameCount() const { return _vmdDecoder->getFramesCount(); }
- Graphics::Surface *decodeNextFrame();
- Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
- byte *getPalette() { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const { return _dirtyPalette; }
-
-protected:
- Common::Rational getFrameRate() const { return _vmdDecoder->getFrameRate(); }
-
-private:
- Graphics::Vmd *_vmdDecoder;
- Audio::Mixer *_mixer;
- Graphics::Surface *_surface;
- Common::SeekableReadStream *_fileStream;
- byte _palette[256 * 3];
- bool _dirtyPalette;
-
- void loadPaletteFromVMD();
-};
-
-} // End of namespace Graphics
-
-#endif
-
-#endif
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 757171b24c..053bf597f8 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -109,10 +109,9 @@ void ScummEngine::loadCJKFont() {
numChar = 8192;
break;
case Common::ZH_TWN:
- if (_game.id == GID_CMI) {
- fontFile = "chinese.fnt";
- numChar = 13630;
- }
+ // Both The DIG and COMI use same font
+ fontFile = "chinese.fnt";
+ numChar = 13630;
break;
case Common::ZH_CNA:
if (_game.id == GID_FT || _game.id == GID_LOOM || _game.id == GID_INDY3 ||
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
index a0975839d6..e0582f79ef 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -62,8 +62,6 @@ ScummDebugger::ScummDebugger(ScummEngine *s)
_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);
@@ -128,7 +126,7 @@ void ScummDebugger::postEnter() {
bool ScummDebugger::Cmd_Restart(int argc, const char **argv) {
_vm->restart();
- _detach_now = true;
+ detach();
return false;
}
@@ -202,7 +200,7 @@ bool ScummDebugger::Cmd_LoadGame(int argc, const char **argv) {
_vm->requestLoad(slot);
- _detach_now = true;
+ detach();
return false;
}
@@ -867,7 +865,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) {
}
_vm->_bootParam = 0;
- _detach_now = true;
+ detach();
} else {
DebugPrintf("Use 'passcode <SEGA CD Passcode>'\n");
@@ -878,9 +876,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) {
bool ScummDebugger::Cmd_ResetCursors(int argc, const char **argv) {
_vm->resetCursors();
-
- _detach_now = true;
-
+ detach();
return false;
}
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 7275caaa1e..9721c75677 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -314,7 +314,7 @@ static Common::Language detectLanguage(const Common::FSList &fslist, byte id) {
case 449787: // 64f3fe479d45b52902cf88145c41d172
return Common::ES_ESP;
}
- } else {
+ } else { // The DIG
switch (size) {
case 248627: // 1fd585ac849d57305878c77b2f6c74ff
return Common::DE_DEU;
@@ -328,6 +328,8 @@ static Common::Language detectLanguage(const Common::FSList &fslist, byte id) {
return Common::ES_ESP;
case 223107: // 64f3fe479d45b52902cf88145c41d172
return Common::JA_JPN;
+ case 180730: // 424fdd60822722cdc75356d921dad9bf
+ return Common::ZH_TWN;
}
}
}
@@ -381,10 +383,12 @@ static void computeGameSettingsFromMD5(const Common::FSList &fslist, const GameF
}
}
-static void detectGames(const Common::FSList &fslist, Common::List<DetectorResult> &results, const char *gameid) {
- DescMap fileMD5Map;
- DetectorResult dr;
- char md5str[32+1];
+static void composeFileHashMap(const Common::FSList &fslist, DescMap &fileMD5Map, int depth, const char **globs) {
+ if (depth <= 0)
+ return;
+
+ if (fslist.empty())
+ return;
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (!file->isDirectory()) {
@@ -392,8 +396,36 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul
d.node = *file;
d.md5Entry = 0;
fileMD5Map[file->getName()] = d;
+ } else {
+ if (!globs)
+ continue;
+
+ bool matched = false;
+ for (const char *glob = *globs; *glob; glob++)
+ if (file->getName().matchString(glob, true)) {
+ matched = true;
+ break;
+ }
+
+ if (!matched)
+ continue;
+
+ Common::FSList files;
+
+ if (file->getChildren(files, Common::FSNode::kListAll)) {
+ composeFileHashMap(files, fileMD5Map, depth - 1, globs);
+ }
}
}
+}
+
+static void detectGames(const Common::FSList &fslist, Common::List<DetectorResult> &results, const char *gameid) {
+ DescMap fileMD5Map;
+ DetectorResult dr;
+ char md5str[32+1];
+
+ // Dive one level down since mac indy3/loom has its files split into directories. See Bug #1438631
+ composeFileHashMap(fslist, fileMD5Map, 2, directoryGlobs);
// Iterate over all filename patterns.
for (const GameFilenamePattern *gfp = gameFilenamesTable; gfp->gameid; ++gfp) {
@@ -458,6 +490,12 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul
// Exact match found. Compute the precise game settings.
computeGameSettingsFromMD5(fslist, gfp, d.md5Entry, dr);
+ // Print some debug info
+ int filesize = tmp->size();
+ if (d.md5Entry->filesize != filesize)
+ debug(1, "SCUMM detector found matching file '%s' with MD5 %s, size %d\n",
+ file.c_str(), md5str, filesize);
+
// Sanity check: We *should* have found a matching gameid / variant at this point.
// If not, then there's a bug in our data tables...
assert(dr.game.gameid != 0);
@@ -866,7 +904,8 @@ GameList ScummMetaEngine::detectGames(const Common::FSList &fslist) const {
}
}
- dg.setGUIOptions(x->game.guioptions);
+ dg.setGUIOptions(x->game.guioptions | MidiDriver::musicType2GUIO(x->game.midi));
+ dg.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
detectedGames.push_back(dg);
}
@@ -966,6 +1005,10 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co
debug(1, "Using MD5 '%s'", res.md5.c_str());
}
+ // If the GUI options were updated, we catch this here and update them in the users config
+ // file transparently.
+ Common::updateGameGUIOptions(res.game.guioptions, getGameGUIOptionsDescriptionLanguage(res.language));
+
// 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.
@@ -982,11 +1025,6 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co
// TODO: Maybe allow the null driver, too?
if (res.game.platform == Common::kPlatformFMTowns && res.game.version == 3)
res.game.midi = MDT_TOWNS;
-
- // If the GUI options were updated, we catch this here and update them in the users config
- // file transparently.
- Common::updateGameGUIOptions(res.game.guioptions);
-
// Finally, we have massaged the GameDescriptor to our satisfaction, and can
// instantiate the appropriate game engine. Hooray!
switch (res.game.version) {
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 0b90af4ec4..d8987c816f 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -48,6 +48,15 @@ namespace Scumm {
#pragma mark --- Tables ---
#pragma mark -
+/**
+ * This table contains list of directories which could contain game data
+ * and which should be looked into during detection.
+ */
+static const char *directoryGlobs[] = {
+ "rooms *", // Mac version of indy3/loom
+ 0
+};
+
/**
* This table contains all game IDs supported by the SCUMM engine, and maps
@@ -215,7 +224,7 @@ static const GameSettings gameVariantsTable[] = {
{"indy3", "VGA", "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
{"indy3", "FM-TOWNS", 0, GID_INDY3, 3, 0, MDT_TOWNS, GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"loom", "EGA", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
+ {"loom", "EGA", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"loom", "No AdLib", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
#ifdef USE_RGB_COLOR
{"loom", "PC-Engine", 0, GID_LOOM, 3, 0, MDT_NONE, GF_AUDIOTRACKS | GF_OLD256 | GF_16BIT_COLOR, Common::kPlatformPCEngine, GUIO_NOSPEECH | GUIO_NOMIDI},
@@ -225,24 +234,24 @@ static const GameSettings gameVariantsTable[] = {
{"pass", 0, 0, GID_PASS, 4, 0, MDT_PCSPK | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"monkey", "VGA", "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
- {"monkey", "EGA", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH},
+ {"monkey", "VGA", "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+ {"monkey", "EGA", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH},
{"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK, GF_16COLOR, Common::kPlatformAtariST, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"monkey2", 0, 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
+ {"monkey2", 0, 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
- {"atlantis", "" , 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NONE},
- {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
+ {"atlantis", "" , 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
+ {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
- {"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NONE},
- {"tentacle", "Floppy", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NOSPEECH},
+ {"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},
+ {"tentacle", "Floppy", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NOSPEECH},
- {"samnmax", "", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NONE},
- {"samnmax", "Floppy", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NOSPEECH},
+ {"samnmax", "", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},
+ {"samnmax", "Floppy", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NOSPEECH},
#ifdef ENABLE_SCUMM_7_8
{"ft", 0, 0, GID_FT, 7, 0, MDT_NONE, 0, UNK, GUIO_NOMIDI},
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 1f153094c1..b160dac6f2 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -26,6 +26,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "common/events.h"
+#include "common/translation.h"
#include "graphics/scaler.h"
@@ -488,9 +489,9 @@ HelpDialog::HelpDialog(const GameSettings &game)
_numPages = ScummHelp::numPages(_game.id);
- _prevButton = new GUI::ButtonWidget(this, "ScummHelp.Prev", "Previous", kPrevCmd, 'P');
- _nextButton = new GUI::ButtonWidget(this, "ScummHelp.Next", "Next", kNextCmd, 'N');
- new GUI::ButtonWidget(this, "ScummHelp.Close", "Close", GUI::kCloseCmd, 'C');
+ _prevButton = new GUI::ButtonWidget(this, "ScummHelp.Prev", _("~P~revious"), 0, kPrevCmd);
+ _nextButton = new GUI::ButtonWidget(this, "ScummHelp.Next", _("~N~ext"), 0, kNextCmd);
+ new GUI::ButtonWidget(this, "ScummHelp.Close", _("~C~lose"), 0, GUI::kCloseCmd);
_prevButton->clearFlags(WIDGET_ENABLED);
_numLines = HELP_NUM_LINES;
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 298917477f..7b0d4909d6 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -1482,7 +1482,7 @@ void GdiV2::prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
// 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
+ // difficult to draw only parts of a room/object. We handle the V2 graphics
// differently from all other (newer) graphic formats for this reason.
//
StripTable *table = (_objectMode ? 0 : _roomStrips);
@@ -2823,7 +2823,7 @@ void GdiPCEngine::decodePCEngineObject(const byte *ptr, int xpos, int ypos, int
free(stripOffsets);
}
-void setTileData(byte *tile, int index, byte byte0, byte byte1) {
+void GdiPCEngine::setTileData(byte *tile, int index, byte byte0, byte byte1) {
int row = index % 8;
int plane = (index / 8) * 2;
int plane02Bit, plane13Bit;
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index 108fd4555d..cdb473a67c 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -322,6 +322,7 @@ protected:
protected:
void decodePCEngineGfx(const byte *room);
void decodeStrip(const byte *ptr, uint16 *tiles, byte *colors, uint16 *masks, int numRows, bool isObject);
+ void setTileData(byte *tile, int index, byte byte0, byte byte1);
void decodePCEngineTileData(const byte *ptr);
void decodePCEngineMaskData(const byte *ptr);
void decodePCEngineObject(const byte *ptr, int xpos, int ypos, int width, int height);
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index efe15f308f..57a842f297 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -421,11 +421,11 @@ Instrument_Roland::Instrument_Roland(Serializer *s) {
memset(&_instrument, 0, sizeof(_instrument));
}
-void Instrument_Roland::saveOrLoad (Serializer *s) {
+void Instrument_Roland::saveOrLoad(Serializer *s) {
if (s->isSaving()) {
- s->saveBytes (&_instrument, sizeof(_instrument));
+ s->saveBytes(&_instrument, sizeof(_instrument));
} else {
- s->loadBytes (&_instrument, sizeof(_instrument));
+ s->loadBytes(&_instrument, sizeof(_instrument));
memcpy(&_instrument_name, &_instrument.common.name, sizeof(_instrument.common.name));
_instrument_name[10] = '\0';
if (!_native_mt32 && getEquivalentGM() >= 128) {
diff --git a/engines/scumm/player_nes.cpp b/engines/scumm/player_nes.cpp
index 4618de3175..96396e7a08 100644
--- a/engines/scumm/player_nes.cpp
+++ b/engines/scumm/player_nes.cpp
@@ -23,6 +23,7 @@
*
*/
+#ifndef DISABLE_NES_APU
#include "engines/engine.h"
#include "scumm/player_nes.h"
@@ -1065,3 +1066,5 @@ byte Player_NES::APU_readStatus() {
}
} // End of namespace Scumm
+
+#endif // DISABLE_NES_APU
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 7362dcd8cf..3cc619f630 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -364,7 +364,7 @@ bool ScummEngine::loadState(int slot, bool compat) {
}
}
- Graphics::skipThumbnailHeader(*in);
+ Graphics::skipThumbnail(*in);
}
// Since version 56 we save additional information about the creation of
@@ -577,6 +577,10 @@ bool ScummEngine::loadState(int slot, bool compat) {
// Fixes bug #1766072: MANIACNES: Music Doesn't Start On Load Game
if (_game.platform == Common::kPlatformNES) {
runScript(5, 0, 0, 0);
+
+ if (VAR(224)) {
+ _sound->addSoundToQueue(VAR(224));
+ }
}
return true;
@@ -717,7 +721,7 @@ bool ScummEngine::loadInfosFromSlot(const char *target, int slot, InfoStuff *stu
return false;
}
- if (!Graphics::skipThumbnailHeader(*in)) {
+ if (!Graphics::skipThumbnail(*in)) {
delete in;
return false;
}
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index b1545db0f3..5c20e0dfd3 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -2594,6 +2594,17 @@ void ScummEngine_v5::decodeParseString() {
else
strcpy((char *)tmpBuf+16, "^19^");
printString(textSlot, tmpBuf);
+ } else if (_game.id == GID_MONKEY_EGA && _roomResource == 30 && vm.slot[_currentScript].number == 411 &&
+ strstr((const char *)_scriptPointer, "NCREDIT-NOTE-AMOUNT")) {
+ // WORKAROUND for bug #3003643 (MI1EGA German: Credit text incorrect)
+ // The script contains buggy text.
+ const char *tmp = strstr((const char *)_scriptPointer, "NCREDIT-NOTE-AMOUNT");
+ char tmpBuf[256];
+ const int diff = tmp - (const char *)_scriptPointer;
+ memcpy(tmpBuf, _scriptPointer, diff);
+ strcpy(tmpBuf + diff, "5000");
+ strcpy(tmpBuf + diff + 4, tmp + sizeof("NCREDIT-NOTE-AMOUNT") - 1);
+ printString(textSlot, (byte *)tmpBuf);
} else {
printString(textSlot, _scriptPointer);
}
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index 9b7e0798eb..a25fac1a88 100644
--- a/engines/scumm/scumm-md5.h
+++ b/engines/scumm/scumm-md5.h
@@ -1,5 +1,5 @@
/*
- This file was generated by the md5table tool on Sun May 9 20:53:55 2010
+ This file was generated by the md5table tool on Sun Jun 27 05:23:26 2010
DO NOT EDIT MANUALLY!
*/
@@ -40,14 +40,14 @@ static const MD5Table md5table[] = {
{ "0a295b80f9a9edf818e8e161a0e83830", "freddi2", "HE 80", "", -1, Common::FR_FRA, Common::kPlatformUnknown },
{ "0a41311d462b6639fc45297b9044bf16", "monkey", "No AdLib", "EGA", -1, Common::ES_ESP, Common::kPlatformAtariST },
{ "0a6d7b81b850ed4a77811c60c9b5c555", "PuttTime", "HE 99", "Mini Game", -1, Common::EN_USA, Common::kPlatformWindows },
- { "0aa050f4ad79402fbe9c4f78fb8ac494", "loom", "PC-Engine", "", -1, Common::EN_ANY, Common::kPlatformPCEngine },
+ { "0aa050f4ad79402fbe9c4f78fb8ac494", "loom", "PC-Engine", "", 6532, Common::EN_ANY, Common::kPlatformPCEngine },
{ "0ab19be9e2a3f6938226638b2a3744fe", "PuttTime", "HE 100", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "0ac41e2e3d2174e5a042a6b565328dba", "puttrace", "HE 98", "Demo", 13110, Common::EN_USA, Common::kPlatformUnknown },
{ "0b3222aaa7efcf283eb621e0cefd26cc", "puttputt", "HE 60", "", -1, Common::RU_RUS, Common::kPlatformPC },
{ "0be88565f734b1e9e77ccaaf3bb14b29", "loom", "EGA", "EGA", -1, Common::ES_ESP, Common::kPlatformPC },
{ "0bf1a3eb198ca1bd2ebe104825cec770", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "0c331637580950aea2346e012ef2a868", "maniac", "V2", "V2", 1988, Common::EN_ANY, Common::kPlatformAtariST },
- { "0c45eb4baff0c12c3d9dfa889c8070ab", "pajama3", "", "Demo", -1, Common::DE_DEU, Common::kPlatformUnknown },
+ { "0c45eb4baff0c12c3d9dfa889c8070ab", "pajama3", "", "Demo", 13884, Common::DE_DEU, Common::kPlatformUnknown },
{ "0cccfa5223099a60e76cfcca57a1a141", "freddi3", "", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "0d1b69471605201ef2fa9cec1f5f02d2", "maniac", "V2", "V2", -1, Common::ES_ESP, Common::kPlatformPC },
{ "0e4c5d54a0ad4b26132e78b5ea76642a", "samnmax", "Floppy", "Demo", 6485, Common::EN_ANY, Common::kPlatformPC },
@@ -75,11 +75,11 @@ static const MD5Table md5table[] = {
{ "15e03ffbfeddb9c2aebc13dcb2a4a8f4", "monkey", "VGA", "VGA", 8357, Common::EN_ANY, Common::kPlatformPC },
{ "15f588e887e857e8c56fe6ade4956168", "atlantis", "Floppy", "Floppy", -1, Common::ES_ESP, Common::kPlatformAmiga },
{ "16542a7342a918bfe4ba512007d36c47", "FreddisFunShop", "HE 99L", "", -1, Common::EN_USA, Common::kPlatformUnknown },
- { "166553538ff320c69edafeee29525419", "samnmax", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "166553538ff320c69edafeee29525419", "samnmax", "", "CD", 199195304, Common::EN_ANY, Common::kPlatformMacintosh },
{ "16effd200aa6b8abe9c569c3e578814d", "freddi4", "HE 99", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows },
{ "179879b6e35c1ead0d93aab26db0951b", "fbear", "HE 70", "", 13381, Common::EN_ANY, Common::kPlatformWindows },
{ "17b5d5e6af4ae89d62631641d66d5a05", "indy3", "VGA", "VGA", -1, Common::IT_ITA, Common::kPlatformPC },
- { "17f7296f63c78642724f057fd8e736a7", "maniac", "NES", "", -1, Common::EN_GRB, Common::kPlatformNES },
+ { "17f7296f63c78642724f057fd8e736a7", "maniac", "NES", "", 2082, Common::EN_GRB, Common::kPlatformNES },
{ "17fa250eb72dae2dad511ba79c0b6b0a", "tentacle", "", "Demo", -1, Common::FR_FRA, Common::kPlatformPC },
{ "182344899c2e2998fca0bebcd82aa81a", "atlantis", "", "CD", 12035, Common::EN_ANY, Common::kPlatformPC },
{ "183d7464902d40d00800e8ee1f04117c", "maniac", "V2", "V2", 1988, Common::DE_DEU, Common::kPlatformPC },
@@ -130,13 +130,14 @@ static const MD5Table md5table[] = {
{ "2d388339d6050d8ccaa757b64633954e", "zak", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns },
{ "2d4536a56e01da4b02eb021e7770afa2", "zak", "FM-TOWNS", "", 7520, Common::EN_ANY, Common::kPlatformFMTowns },
{ "2d4acbdcfd8e374c9da8c2e7303a5cd0", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
- { "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformApple2GS },
{ "2d9d46f23cb07bbc90b8ad464d3e4ff8", "atlantis", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "2e8a1f76ea33bc5e04347646feee173d", "pajama3", "", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "2fe369ad70f52a8cf7ad6077ee64f81a", "loom", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "305d3dd57c96c65b017bc70c8c7cfb5e", "monkey", "CD", "CD", 8955, Common::DE_DEU, Common::kPlatformPC },
{ "30ba1e825d4ad2b448143ae8df18482a", "pajama2", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown },
+ { "30d1903b0715759af064be2127381cd0", "freddi", "HE 100", "", 34837, Common::DE_DEU, Common::kPlatformWii },
{ "319a4dde52c7960b5aae8a1ec348d918", "monkey", "VGA", "VGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "31aa57f460a3d12429f0552a46a90b39", "puttputt", "Demo", "Demo", 6150, Common::EN_ANY, Common::kPlatformPC },
{ "31b8fda4c8c7413fa6b39997e776eba4", "loom", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
@@ -145,7 +146,7 @@ static const MD5Table md5table[] = {
{ "330f631502e381a4e199a3f7cb483c20", "indy3", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "33e989f85da700e2014d00f345cab3d7", "puttrace", "HE 98.5", "", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "3433be9866ca4261b2d5d25374e3f243", "monkey", "VGA", "VGA", -1, Common::FR_FRA, Common::kPlatformAmiga },
- { "3486ede0f904789267d4bcc5537a46d4", "puttzoo", "", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "3486ede0f904789267d4bcc5537a46d4", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformMacintosh },
{ "356fb5f680b68251333016175935d126", "BluesABCTime", "HE CUP", "Preview", 4133436, Common::UNK_LANG, Common::kPlatformUnknown },
{ "35a2d3040fa512f8232d9e443319d84d", "dig", "", "", 659335495, Common::EN_ANY, Common::kPlatformMacintosh },
{ "362c1d281fb9899254cda66ad246c66a", "dig", "Demo", "Demo", 3472, Common::EN_ANY, Common::kPlatformUnknown },
@@ -167,21 +168,24 @@ static const MD5Table md5table[] = {
{ "3a3e592b074f595489f7f11e150c398d", "puttzoo", "HE 99", "Updated", -1, Common::EN_USA, Common::kPlatformWindows },
{ "3a5d13675e9a23aedac0bac7730f0ac1", "samnmax", "", "CD", -1, Common::FR_FRA, Common::kPlatformMacintosh },
{ "3a5ec90d556d4920976c5578bfbfaf79", "maniac", "NES", "", -1, Common::DE_DEU, Common::kPlatformNES },
+ { "3ae7f002d9256b8bdf76aaf8a3a069f8", "freddi", "HE 100", "", 34837, Common::EN_GRB, Common::kPlatformWii },
{ "3af61c5edf8e15b43dbafd285b2e9777", "puttcircus", "", "Demo", -1, Common::HE_ISR, Common::kPlatformWindows },
{ "3b301b7892f883ce42ab4be6a274fea6", "samnmax", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformPC },
{ "3b832f4a90740bf22e9b8ed42ca0128c", "freddi4", "HE 99", "", -1, Common::EN_GRB, Common::kPlatformWindows },
+ { "3c4c471342bd95505a42334367d8f127", "puttmoon", "HE 70", "", 12161, Common::RU_RUS, Common::kPlatformWindows },
{ "3cce1913a3bc586b51a75c3892ff18dd", "indy3", "VGA", "VGA", -1, Common::RU_RUS, Common::kPlatformPC },
{ "3d219e7546039543307b55a91282bf18", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatformPC },
{ "3de99ef0523f8ca7958faa3afccd035a", "spyfox", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "3df6ead57930488bc61e6e41901d0e97", "fbear", "HE 62", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "3e48298920fab9b7aec5a971e1bd1fab", "pajama3", "", "Demo", -1, Common::EN_GRB, Common::kPlatformWindows },
+ { "3e861421f494711bc6f619d4aba60285", "airport", "", "", 93231, Common::RU_RUS, Common::kPlatformWindows },
{ "40564ec47da48a67787d1f9bd043902a", "maniac", "V2 Demo", "V2 Demo", 1988, Common::EN_ANY, Common::kPlatformPC },
{ "4167a92a1d46baa4f4127d918d561f88", "tentacle", "", "CD", 7932, Common::EN_ANY, Common::kPlatformUnknown },
{ "41958e24d03181ff9a381a66d048a581", "ft", "", "", -1, Common::PT_BRA, Common::kPlatformUnknown },
{ "425205754fa749f4f0b0dd9d09fa45fd", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "430bc518017b6fac046f58bab6baad5d", "monkey2", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
{ "439a7f4adf510489981ac52308e7d7a2", "maniac", "C64", "", -1, Common::DE_DEU, Common::kPlatformC64 },
- { "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", 18422, Common::EN_ANY, Common::kPlatformUnknown },
{ "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", "VGA", "VGA", 8357, Common::ES_ESP, Common::kPlatformPC },
{ "4521138d15d1fd7649c31fb981746231", "pajama2", "HE 98.5", "Demo", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "46b53fd430adcfbed791b48a0d4b079f", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatformPC },
@@ -194,6 +198,7 @@ static const MD5Table md5table[] = {
{ "49a1739981a89066b1121fac04b710f4", "spyfox2", "HE CUP", "Preview", 5756234, Common::UNK_LANG, Common::kPlatformUnknown },
{ "4aa93cb30e485b728504ba3a693f12bf", "pajama", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "4af4a6b248103c1fe9edef619677f540", "puttmoon", "", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "4afb734df8315ee412669c812d4cf0a1", "freddi", "HE 100", "", 34837, Common::FR_FRA, Common::kPlatformWii },
{ "4ba37f835be11a59d969f90f272f575b", "water", "HE 80", "", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "4ba7fb331296c283e73d8f5b2096e551", "samnmax", "", "CD", -1, Common::ES_ESP, Common::kPlatformUnknown },
{ "4bedb49943df95a9c900a5a82ccbe9de", "ft", "", "", -1, Common::FR_FRA, Common::kPlatformUnknown },
@@ -223,11 +228,12 @@ static const MD5Table md5table[] = {
{ "5262a27afcaee04e5c4900220bd463e7", "PuttsFunShop", "", "", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "52a4bae0746a11d7b1e8554e91a6645c", "zak", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformPC },
{ "53e94115b55dd51d4b8ff0871aa1df1e", "spyfox", "", "Demo", 20103, Common::EN_ANY, Common::kPlatformUnknown },
- { "54a936ad06161ff7bfefcb96200f7bff", "monkey", "VGA", "VGA Demo", -1, Common::EN_ANY, Common::kPlatformAmiga },
+ { "54a936ad06161ff7bfefcb96200f7bff", "monkey", "VGA", "VGA Demo", 7617, Common::EN_ANY, Common::kPlatformAmiga },
{ "55518cd73cf9c6d23ea29c51ee06bdfe", "ft", "", "", -1, Common::IT_ITA, Common::kPlatformUnknown },
{ "55e4cc866ff9046824e1c638ba2b8c7f", "ft", "", "", -1, Common::RU_RUS, Common::kPlatformUnknown },
{ "55f4e9402bec2bded383843123f37c5c", "pajama2", "HE 98.5", "", -1, Common::DE_DEU, Common::kPlatformWindows },
{ "566165a7338fa11029e7c14d94fa70d0", "freddi", "HE 73", "Demo", 9800, Common::EN_ANY, Common::kPlatformWindows },
+ { "56b5922751be7ffd771b38dda56b028b", "freddi", "HE 100", "", 34837, Common::NL_NLD, Common::kPlatformWii },
{ "5719fc8a13b4638b78d9d8d12f091f94", "puttrace", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "5798972220cd458be2626d54c80f71d7", "atlantis", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformAmiga },
{ "57a17febe2183f521250e55d55b83e60", "PuttTime", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformWindows },
@@ -240,9 +246,10 @@ static const MD5Table md5table[] = {
{ "59d5cfcc5e672a6e07baae01328b918b", "PuttTime", "HE 90", "Demo", -1, Common::FR_FRA, Common::kPlatformUnknown },
{ "5a35e36fd777e9c37a49c5b2faca52f9", "loom", "EGA", "EGA Demo", 6108, Common::EN_ANY, Common::kPlatformPC },
{ "5b08000a9c47b2887df6506ac767ca68", "fbear", "HE 62", "", -1, Common::EN_ANY, Common::kPlatform3DO },
- { "5bd335265a61caa3d78956ad9f88ba23", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "5bd335265a61caa3d78956ad9f88ba23", "football", "", "Demo", 23135, Common::EN_ANY, Common::kPlatformUnknown },
{ "5c21fc49aee8f46e58fef21579e614a1", "thinker1", "", "", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "5d88b9d6a88e6f8e90cded9d01b7f082", "loom", "VGA", "VGA", 8307, Common::EN_ANY, Common::kPlatformPC },
+ { "5dda73606533d66a4c3f4f9ea6e842af", "farm", "", "", 87061, Common::RU_RUS, Common::kPlatformWindows },
{ "5e8fb66971a60e523e5afbc4c129c0e8", "socks", "HE 85", "", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "5ebb57234b2fe5c5dff641e00184ad81", "freddi", "HE 73", "", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "5fbe557049892eb4b709d90916ec97ca", "indy3", "EGA", "EGA", 5361, Common::EN_ANY, Common::kPlatformPC },
@@ -354,6 +361,7 @@ static const MD5Table md5table[] = {
{ "87df3e0074624040407764b7c5e710b9", "pajama", "", "Demo", 18354, Common::NL_NLD, Common::kPlatformWindows },
{ "87f6e8037b7cc996e13474b491a7a98e", "maniac", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformPC },
{ "8801fb4a1200b347f7a38523339526dd", "jungle", "", "", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "880c5ca5b944648b3f8b03feb41705a8", "freddi", "HE 100", "", 34837, Common::SE_SWE, Common::kPlatformWii },
{ "883af4b0af4f77a92f1dcf1d0a283140", "tentacle", "", "CD", -1, Common::ES_ESP, Common::kPlatformUnknown },
{ "898ce8eb1234a955ef75e87141902bb3", "freddi3", "", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "898eaa21f79cf8d4f08db856244689ff", "pajama", "HE 99", "Updated", 66505, Common::EN_ANY, Common::kPlatformWindows },
@@ -363,13 +371,13 @@ static const MD5Table md5table[] = {
{ "8aed489aba45d2b9fb8a04079c9c6e6a", "baseball", "HE CUP", "Preview", 12876596, Common::UNK_LANG, Common::kPlatformUnknown },
{ "8afb3cf9f95abf208358e984f0c9e738", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatform3DO },
{ "8bdb0bf87b5e303dd35693afb9351215", "ft", "", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
- { "8d479e36f35e80257dfc102cf4b8a912", "farm", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "8d479e36f35e80257dfc102cf4b8a912", "farm", "HE 72", "Demo", 34333, Common::EN_ANY, Common::kPlatformWindows },
{ "8de13897f0121c79d29a2377159f9ad0", "socks", "HE 99", "Updated", -1, Common::EN_ANY, Common::kPlatformWindows },
{ "8e3241ddd6c8dadf64305e8740d45e13", "balloon", "HE 100", "Updated", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "8e4ee4db46954bfe2912e259a16fad82", "monkey2", "", "", -1, Common::FR_FRA, Common::kPlatformPC },
{ "8e9417564f33790815445b2136efa667", "atlantis", "", "CD", 11915, Common::JA_JPN, Common::kPlatformMacintosh },
{ "8e9830a6f2702be5b22c8fa0a6aaf977", "freddi2", "HE 80", "", -1, Common::NL_NLD, Common::kPlatformMacintosh },
- { "8eb84cee9b429314c7f0bdcf560723eb", "monkey", "FM-TOWNS", "", -1, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "8eb84cee9b429314c7f0bdcf560723eb", "monkey", "FM-TOWNS", "", 9925, Common::EN_ANY, Common::kPlatformFMTowns },
{ "8ee63cafb1fe9d62aa0d5a23117e70e7", "freddi2", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "8f3758ff98c9c5d78e5d635222cad026", "atlantis", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformPC },
{ "8fec68383202d38c0d25e9e3b757c5df", "comi", "Demo", "Demo", 18041, Common::UNK_LANG, Common::kPlatformWindows },
@@ -410,7 +418,7 @@ static const MD5Table md5table[] = {
{ "9d7b67be003fea60be4dcbd193611936", "ft", "Demo", "Demo", 11164, Common::EN_ANY, Common::kPlatformMacintosh },
{ "9dc02577bf50d4cfaf3de3fbac06fbe2", "puttmoon", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "9e5e0fb43bd22f4628719b7501adb717", "monkey", "No AdLib", "EGA", -1, Common::FR_FRA, Common::kPlatformAtariST },
- { "9fd66fb3b04703bd50da4356e4202558", "spyfox2", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "9fd66fb3b04703bd50da4356e4202558", "spyfox2", "", "", 51295, Common::EN_ANY, Common::kPlatformMacintosh },
{ "a00554c31d623fdb9fcb0f924b89b42b", "loom", "EGA", "EGA Demo", -1, Common::EN_ANY, Common::kPlatformPC },
{ "a01fab4a64d47b96e2e58e6b0f825cc7", "monkey", "VGA", "VGA", 8347, Common::FR_FRA, Common::kPlatformPC },
{ "a095616d2d23ccf43b8e257711202cba", "football2002", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
@@ -427,6 +435,7 @@ static const MD5Table md5table[] = {
{ "a561d2e2413cc1c71d5a1bf87bf493ea", "lost", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "a56e8d9d4281c53c3f63c9bd22a59e21", "catalog", "HE CUP", "Preview", 10978342, Common::EN_ANY, Common::kPlatformUnknown },
{ "a570381b028972d891052ee1e51dc011", "maniac", "V2", "V2", 1988, Common::EN_ANY, Common::kPlatformAtariST },
+ { "a59a438cb182124c30c4447d8ed469e9", "freddi", "HE 100", "", 34837, Common::NB_NOR, Common::kPlatformWii },
{ "a5c5388da9bf0e6662fdca8813a79d13", "farm", "", "", 86962, Common::EN_ANY, Common::kPlatformWindows },
{ "a654fb60c3b67d6317a7894ffd9f25c5", "pajama3", "", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "a7cacad9c40c4dc9e1812abf6c8af9d5", "puttcircus", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
@@ -486,13 +495,13 @@ static const MD5Table md5table[] = {
{ "c4787c3e8b5e2dfda90850ee800af00f", "zak", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformPC },
{ "c4ffae9fac495475d6bc3343ccc8faf9", "Soccer2004", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "c5cc7cba02a2fbd539c4439e775b0536", "puttzoo", "HE 99", "Updated", 43470, Common::DE_DEU, Common::kPlatformWindows },
- { "c5d10e190d4b4d59114b824f2fdbd00e", "loom", "FM-TOWNS", "", -1, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "c5d10e190d4b4d59114b824f2fdbd00e", "loom", "FM-TOWNS", "", 7540, Common::EN_ANY, Common::kPlatformFMTowns },
{ "c63ee46143ba65f9ce14cf539ca51bd7", "atlantis", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformPC },
{ "c666a998af90d81db447eccba9f72c8d", "monkey", "No AdLib", "EGA", -1, Common::EN_ANY, Common::kPlatformAtariST },
{ "c6907d44f1166941d982864cd42cdc89", "pajama2", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "c782fbbe74a987c3df8ac73cd3e289ed", "freddi", "HE 73", "", -1, Common::SE_SWE, Common::kPlatformMacintosh },
{ "c7890e038806df2bb5c0c8c6f1986ea2", "monkey", "VGA", "VGA", -1, Common::EN_ANY, Common::kPlatformPC },
- { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "", "", -1, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns },
{ "c7c492a107ec520d7a7943037d0ca54a", "freddi", "HE 71", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows },
{ "c83079157ec765a28de445aec9768d60", "tentacle", "", "Demo", 7477, Common::EN_ANY, Common::kPlatformUnknown },
{ "c8575e0b973ff1723aba6cd92c642db2", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
@@ -516,7 +525,7 @@ static const MD5Table md5table[] = {
{ "cf4ef315214c7d8cdab6302cdb7e50db", "freddi", "HE 73", "Demo", -1, Common::DE_DEU, Common::kPlatformWindows },
{ "cf8d13446ec6cb6222287a925fd47c1d", "baseball", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "cf8ef3a1fb483c5c4b1c584d1167b2c4", "freddi", "HE 73", "", -1, Common::DE_DEU, Common::kPlatformWindows },
- { "cf90b4db5486ef798db78fe6fbf897e5", "pajama3", "", "Demo", -1, Common::EN_USA, Common::kPlatformWindows },
+ { "cf90b4db5486ef798db78fe6fbf897e5", "pajama3", "", "Demo", 13902, Common::EN_USA, Common::kPlatformWindows },
{ "d00ffc8c32d17e575fd985d435d2eb88", "arttime", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "d0549508a06bbb9f99ed19c9e97891f3", "football2002", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "d06fbe28818fef7bfc45c2cdf0c0849d", "zak", "V2", "V2", -1, Common::DE_DEU, Common::kPlatformPC },
@@ -551,14 +560,14 @@ static const MD5Table md5table[] = {
{ "dbf4d59d70b826733f379f998354d350", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "dcf0119a90451a7d6e0f1920931ba130", "freddi4", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "dd30a53035393baa5a5e222e716559af", "maniac", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformAtariST },
- { "de4efb910210736813c9a1185384bace", "puttzoo", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "de4efb910210736813c9a1185384bace", "puttzoo", "HE 72", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows },
{ "debe337f73d660e951ece7c1f1c81add", "zak", "V2", "V2", -1, Common::EN_ANY, Common::kPlatformPC },
{ "defb8cb9ec4b0f91acfb6b61c6129ad9", "PuttTime", "HE 99", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "df03ee021aa9b81d90cab9c26da07614", "indy3", "EGA", "EGA", -1, Common::IT_ITA, Common::kPlatformAmiga },
{ "df047cc4792150f601290357566d36a6", "freddi", "HE 90", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "e01acc8c12ef44e8f778fe87e5f90f4e", "fbpack", "", "", -1, Common::EN_ANY, Common::kPlatform3DO },
{ "e03ed1474ec14de78359970e0457a820", "freddi4", "HE 99", "Demo", -1, Common::EN_GRB, Common::kPlatformWindows },
- { "e144f5f49d9241d2a9dee2576b3d09cb", "airport", "", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "e144f5f49d9241d2a9dee2576b3d09cb", "airport", "", "Demo", 51152, Common::EN_ANY, Common::kPlatformWindows },
{ "e17db1ddf91b39ca6bbc8ad3ed19e883", "monkey", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
{ "e246e02db9630533a40d99c9f54a8e01", "monkey2", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "e361a7058ed8e8ebb462663c0a3ae8d6", "puttputt", "HE 62", "", -1, Common::HE_ISR, Common::kPlatformPC },
@@ -573,12 +582,13 @@ static const MD5Table md5table[] = {
{ "e6cd81b25ab1453a8a6d3482118c391e", "pass", "", "", 7857, Common::EN_ANY, Common::kPlatformPC },
{ "e72bb4c2b613db2cf50f89ff6350e70a", "ft", "", "", -1, Common::ES_ESP, Common::kPlatformUnknown },
{ "e781230da44a44e2f0770edb2b3b3633", "maniac", "V2", "V2", -1, Common::EN_ANY, Common::kPlatformAmiga },
+ { "e8d0697906e53fee8b7e9f5652696da8", "atlantis", "", "CD", 11915, Common::JA_JPN, Common::kPlatformPC },
{ "e94c7cc3686fce406d3c91b5eae5a72d", "zak", "V2", "V2", -1, Common::EN_ANY, Common::kPlatformAmiga },
{ "e95cf980719c0be078fb68a67af97b4a", "funpack", "", "", -1, Common::JA_JPN, Common::kPlatform3DO },
{ "e98b982ceaf9d253d730bde8903233d6", "monkey", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformPC },
{ "eae95b2b3546d8ba86ae1d397c383253", "dog", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "eb700bb73ca1cc44a1ad5e4b1a4bdeaf", "indy3", "EGA", "EGA", 5361, Common::DE_DEU, Common::kPlatformPC },
- { "ebd0b2c8a387f18887282afe6cad894a", "spyozon", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "ebd0b2c8a387f18887282afe6cad894a", "spyozon", "", "Demo", 15317, Common::EN_ANY, Common::kPlatformUnknown },
{ "ebd324dcf06a4c49e1ba5c231eee1060", "freddi4", "HE 99", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "ecc4340c2b801f5af8da4e00c0e432d9", "puttcircus", "", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "ed2b074bc3166087a747acb2a3c6abb0", "freddi3", "HE 98.5", "Demo", -1, Common::DE_DEU, Common::kPlatformUnknown },
@@ -614,7 +624,7 @@ static const MD5Table md5table[] = {
{ "fbbbb38a81fc9d6a61d509278390a290", "farm", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "fbdd947d21e8f5bac6d6f7a316af1c5a", "spyfox", "", "Demo", 15693, Common::EN_ANY, Common::kPlatformUnknown },
{ "fc53ce0e5f6562b1c1e1b4b8203acafb", "samnmax", "Floppy", "Floppy", -1, Common::ES_ESP, Common::kPlatformPC },
- { "fc6b6148e80d67939d9a18697c0f626a", "monkey", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformPC },
+ { "fc6b6148e80d67939d9a18697c0f626a", "monkey", "EGA", "EGA", 8367, Common::DE_DEU, Common::kPlatformPC },
{ "fc8d197a22146e74766e9cb0cfcaf1da", "freddi2", "HE 80", "Demo", 27298, Common::EN_ANY, Common::kPlatformUnknown },
{ "fcb78ebecab2757264c590890c319cc5", "PuttTime", "HE 85", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "fce4b8010704b103acfeea9413788f32", "freddi2", "HE 80", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 2359d4a04f..dcbe4e6c0a 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1631,28 +1631,28 @@ void ScummEngine_v100he::resetScumm() {
#endif
void ScummEngine::setupMusic(int midi) {
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(midi);
- _native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(midi);
+ _native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
- switch (midiDriver) {
- case MD_NULL:
+ switch (MidiDriver::getMusicType(dev)) {
+ case MT_NULL:
_musicType = MDT_NONE;
break;
- case MD_PCSPK:
- case MD_PCJR:
+ case MT_PCSPK:
+ case MT_PCJR:
_musicType = MDT_PCSPK;
break;
- case MD_CMS:
+ //case MT_CMS:
#if 1
_musicType = MDT_ADLIB;
#else
_musicType = MDT_CMS; // Still has number of bugs, disable by default
#endif
break;
- case MD_TOWNS:
+ case MT_TOWNS:
_musicType = MDT_TOWNS;
break;
- case MD_ADLIB:
+ case MT_ADLIB:
_musicType = MDT_ADLIB;
break;
default:
@@ -1704,7 +1704,7 @@ void ScummEngine::setupMusic(int midi) {
if (!_mixer->isReady()) {
warning("Sound mixer initialization failed");
if (_musicType == MDT_ADLIB || _musicType == MDT_PCSPK || _musicType == MDT_CMS) {
- midiDriver = MD_NULL;
+ dev = 0;
_musicType = MDT_NONE;
warning("MIDI driver depends on sound mixer, switching to null MIDI driver");
}
@@ -1720,7 +1720,9 @@ void ScummEngine::setupMusic(int midi) {
_musicEngine = new Player_SID(this, _mixer);
#endif
} else if (_game.platform == Common::kPlatformNES && _game.version == 1) {
+#ifndef DISABLE_NES_APU
_musicEngine = new Player_NES(this, _mixer);
+#endif
} else if (_game.platform == Common::kPlatformAmiga && _game.version == 2) {
_musicEngine = new Player_V2A(this, _mixer);
} else if (_game.platform == Common::kPlatformAmiga && _game.version == 3) {
@@ -1732,11 +1734,11 @@ void ScummEngine::setupMusic(int midi) {
} else if (_game.platform == Common::kPlatformAmiga && _game.version <= 4) {
_musicEngine = new Player_V4A(this, _mixer);
} else if (_game.id == GID_MANIAC && _game.version == 1) {
- _musicEngine = new Player_V1(this, _mixer, midiDriver != MD_PCSPK);
+ _musicEngine = new Player_V1(this, _mixer, MidiDriver::getMusicType(dev) != MT_PCSPK);
} else if (_game.version <= 2) {
- _musicEngine = new Player_V2(this, _mixer, midiDriver != MD_PCSPK);
+ _musicEngine = new Player_V2(this, _mixer, MidiDriver::getMusicType(dev) != MT_PCSPK);
} else if ((_musicType == MDT_PCSPK) && (_game.version > 2 && _game.version <= 4)) {
- _musicEngine = new Player_V2(this, _mixer, midiDriver != MD_PCSPK);
+ _musicEngine = new Player_V2(this, _mixer, MidiDriver::getMusicType(dev) != MT_PCSPK);
} else if (_musicType == MDT_CMS) {
_musicEngine = new Player_V2CMS(this, _mixer);
} else if (_game.platform == Common::kPlatform3DO && _game.heversion <= 62) {
@@ -1746,12 +1748,12 @@ void ScummEngine::setupMusic(int midi) {
MidiDriver *adlibMidiDriver = 0;
if (_musicType != MDT_ADLIB)
- nativeMidiDriver = MidiDriver::createMidi(midiDriver);
+ nativeMidiDriver = MidiDriver::createMidi(dev);
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();
+ adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB));
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
}
@@ -1766,7 +1768,7 @@ void ScummEngine::setupMusic(int midi) {
// YM2162 driver can't handle midi->getPercussionChannel(), NULL shouldn't init MT-32/GM/GS
if ((midi != MDT_TOWNS) && (midi != MDT_NONE)) {
_imuse->property(IMuse::PROP_NATIVE_MT32, _native_mt32);
- if (midiDriver != MD_MT32) // MT-32 Emulation shouldn't be GM/GS initialized
+ if (MidiDriver::getMusicType(dev) != MT_MT32) // MT-32 Emulation shouldn't be GM/GS initialized
_imuse->property(IMuse::PROP_GS, _enable_gs);
}
if (_game.heversion >= 60 || midi == MDT_TOWNS) {
@@ -1837,8 +1839,7 @@ Common::Error ScummEngine::go() {
while (!shouldQuit()) {
- if (_debugger->isAttached())
- _debugger->onFrame();
+ _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.
@@ -2078,6 +2079,12 @@ void ScummEngine::scummLoop_updateScummVars() {
if (_game.version >= 7) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x;
VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
+ } else if (_game.platform == Common::kPlatformNES) {
+ // WORKAROUND:
+ // Since there are 2 2-stripes wide borders in MM NES screen,
+ // we have to compensate for it here. This fixes paning effects.
+ // Fixes bug #1328120: "MANIACNES: Screen width incorrect, camera halts sometimes"
+ VAR(VAR_CAMERA_POS_X) = (camera._cur.x >> V12_X_SHIFT) + 2;
} else if (_game.version <= 2) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x >> V12_X_SHIFT;
} else {
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 65c50aff14..a845f623c2 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1789,7 +1789,7 @@ static void convertADResource(ResourceManager *res, const GameSettings& game, in
// There is a constant delay of ppqn/3 before the music starts.
if (ppqn / 3 >= 128)
- *ptr++ = (ppqn / 3 >> 7) | 0x80;
+ *ptr++ = ((ppqn / 3) >> 7) | 0x80;
*ptr++ = ppqn / 3 & 0x7f;
// Now copy the actual music data
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 480e18e514..30281cb565 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -1110,6 +1110,8 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
}
num += (_game.version == 8) ? 4 : 2;
}
+ } else if (_game.id == GID_DIG && (chr == 1 || chr == 2 || chr == 3 || chr == 8)) {
+ // Skip these characters
} else {
if (!(chr == '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) ||
(_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN))
@@ -1351,6 +1353,8 @@ void ScummEngine_v7::loadLanguageBundle() {
// File contains Korean text (Hangul). just ignore it
} else if (*ptr == 'j') {
// File contains Japanese text. just ignore it
+ } else if (*ptr == 'c') {
+ // File contains Chinese text. just ignore it
} else if (*ptr == 'e') {
// File is encoded!
enc = 0x13;
diff --git a/engines/scumm/verbs.h b/engines/scumm/verbs.h
index 96a49a7ced..83e924edac 100644
--- a/engines/scumm/verbs.h
+++ b/engines/scumm/verbs.h
@@ -31,7 +31,7 @@
namespace Scumm {
/**
- * The area in which some click (or key press) occured and which is passed
+ * The area in which some click (or key press) occurred and which is passed
* to the input script.
*/
enum ClickArea {
diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp
index 9ea20aafc6..edf96f8e8c 100644
--- a/engines/sky/sky.cpp
+++ b/engines/sky/sky.cpp
@@ -185,8 +185,7 @@ Common::Error SkyEngine::go() {
uint32 delayCount = _system->getMillis();
while (!shouldQuit()) {
- if (_debugger->isAttached())
- _debugger->onFrame();
+ _debugger->onFrame();
if (shouldPerformAutoSave(_lastSaveTime)) {
if (_skyControl->loadSaveAllowed()) {
@@ -259,16 +258,16 @@ Common::Error SkyEngine::init() {
_systemVars.gameVersion = _skyDisk->determineGameVersion();
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MIDI);
- if (midiDriver == MD_ADLIB) {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32);
+ if (MidiDriver::getMusicType(dev) == MT_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);
+ if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"))
+ _skyMusic = new MT32Music(MidiDriver::createMidi(dev), _skyDisk);
else
- _skyMusic = new GmMusic(MidiDriver::createMidi(midiDriver), _skyDisk);
+ _skyMusic = new GmMusic(MidiDriver::createMidi(dev), _skyDisk);
}
if (isCDVersion()) {
diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp
index d6a04513a8..8d9ca85829 100644
--- a/engines/sword1/control.cpp
+++ b/engines/sword1/control.cpp
@@ -1175,8 +1175,7 @@ bool Control::restoreGameFromFile(uint8 slot) {
if (saveVersion < 2) // These older version of the savegames used a flag to signal presence of thumbnail
inf->skip(1);
- if (Graphics::checkThumbnailHeader(*inf))
- Graphics::skipThumbnailHeader(*inf);
+ Graphics::skipThumbnail(*inf);
inf->readUint32BE(); // save date
inf->readUint16BE(); // save time
diff --git a/engines/sword1/memman.h b/engines/sword1/memman.h
index 845a8638a2..b489eae2f9 100644
--- a/engines/sword1/memman.h
+++ b/engines/sword1/memman.h
@@ -42,11 +42,7 @@ struct MemHandle {
#define MEM_CAN_FREE 1
#define MEM_DONT_FREE 2
-#ifdef PALMOS_MODE
-#define MAX_ALLOC (3*1024*1024) // max amount of mem we want to alloc().
-#else
#define MAX_ALLOC (6*1024*1024) // max amount of mem we want to alloc().
-#endif
class MemMan {
public:
diff --git a/engines/sword1/music.cpp b/engines/sword1/music.cpp
index 91c943472c..23cc30e4b1 100644
--- a/engines/sword1/music.cpp
+++ b/engines/sword1/music.cpp
@@ -107,7 +107,7 @@ bool MusicHandle::play(const char *fileBase, bool loop) {
if (!_audioSource) {
sprintf(fileName, "%s.aif", fileBase);
if (_file.open(fileName))
- _audioSource = Audio::makeLoopingAudioStream(Audio::makeAIFFStream(_file), loop ? 0 : 1);
+ _audioSource = Audio::makeLoopingAudioStream(Audio::makeAIFFStream(&_file, DisposeAfterUse::NO), loop ? 0 : 1);
}
if (!_audioSource)
diff --git a/engines/sword2/anims.cpp b/engines/sword2/anims.cpp
index 1bf3967047..7fd36fcc86 100644
--- a/engines/sword2/anims.cpp
+++ b/engines/sword2/anims.cpp
@@ -56,8 +56,6 @@ int Router::doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool revers
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!
@@ -75,12 +73,8 @@ int Router::doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool revers
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
@@ -88,8 +82,6 @@ int Router::doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool revers
return IR_STOP;
}
- _vm->_resman->closeResource(animRes);
-
// switch on the sprite
setSpriteStatus(ob_graph, SORT_SPRITE);
}
diff --git a/engines/sword2/music.cpp b/engines/sword2/music.cpp
index a68baed097..c052aa6b46 100644
--- a/engines/sword2/music.cpp
+++ b/engines/sword2/music.cpp
@@ -136,10 +136,8 @@ static Audio::AudioStream *getAudioStream(SoundFileHandle *fh, const char *base,
return NULL;
}
if (fh->fileSize != fh->file.size()) {
- if (fh->idxTab) {
- free(fh->idxTab);
- fh->idxTab = NULL;
- }
+ free(fh->idxTab);
+ fh->idxTab = NULL;
}
} else
alreadyOpen = true;
diff --git a/engines/sword2/resman.h b/engines/sword2/resman.h
index 72bdf73b98..dcc79927ea 100644
--- a/engines/sword2/resman.h
+++ b/engines/sword2/resman.h
@@ -32,11 +32,7 @@ namespace Common {
class File;
}
-#ifdef PALMOS_MODE
-#define MAX_MEM_CACHE (4 * 1024 * 1024) // 4 seems to be enough, 8 = out of memory
-#else
#define MAX_MEM_CACHE (8 * 1024 * 1024) // we keep up to 8 megs of resource data files in memory
-#endif
#define MAX_res_files 20
namespace Sword2 {
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 29f567d7ef..3cdab2bd2b 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -326,12 +326,20 @@ void Sword2Engine::registerDefaultSettings() {
}
void Sword2Engine::syncSoundSettings() {
- // Sound settings. At the time of writing, not all of these can be set
- // by the global options dialog, but it seems silly to split them up.
_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"));
+
+ // Our own settings dialog can mute the music, speech and sound effects
+ // individually. ScummVM's settings dialog has one master mute setting.
+
+ if (ConfMan.getBool("mute")) {
+ ConfMan.setBool("music_mute", true);
+ ConfMan.setBool("speech_mute", true);
+ ConfMan.setBool("sfx_mute", true);
+ }
+
_sound->muteMusic(ConfMan.getBool("music_mute"));
_sound->muteSpeech(ConfMan.getBool("speech_mute"));
_sound->muteFx(ConfMan.getBool("sfx_mute"));
@@ -356,6 +364,13 @@ void Sword2Engine::writeSettings() {
ConfMan.setBool("object_labels", _mouse->getObjectLabels());
ConfMan.setInt("reverse_stereo", _sound->isReverseStereo());
+ // If even one sound type is unmuted, we can't say that all sound is
+ // muted.
+
+ if (!_sound->isMusicMute() || !_sound->isSpeechMute() || !_sound->isFxMute()) {
+ ConfMan.setBool("mute", false);
+ }
+
ConfMan.flushToDisk();
}
@@ -458,8 +473,7 @@ Common::Error Sword2Engine::run() {
_screen->initialiseRenderCycle();
while (1) {
- if (_debugger->isAttached())
- _debugger->onFrame();
+ _debugger->onFrame();
#ifdef SWORD2_DEBUG
if (_stepOneCycle) {
diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp
index 258bd982ed..a2dab9658d 100644
--- a/engines/teenagent/detection.cpp
+++ b/engines/teenagent/detection.cpp
@@ -91,7 +91,9 @@ static const ADParams detectionParams = {
"teenagent",
0,
0,
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ 1,
+ 0
};
#define MAX_SAVES 20
diff --git a/engines/teenagent/module.mk b/engines/teenagent/module.mk
index 2c04c99376..01ba3c79cb 100644
--- a/engines/teenagent/module.mk
+++ b/engines/teenagent/module.mk
@@ -1,23 +1,23 @@
MODULE := engines/teenagent
MODULE_OBJS := \
+ actor.o \
+ animation.o \
+ callbacks.o \
+ console.o \
detection.o \
- teenagent.o \
- resources.o \
+ dialog.o \
+ font.o \
+ inventory.o \
+ music.o \
+ objects.o \
pack.o \
- segment.o \
+ resources.o \
scene.o \
- animation.o \
- font.o \
+ segment.o \
surface.o \
surface_list.o \
- actor.o \
- callbacks.o \
- inventory.o \
- objects.o \
- music.o \
- console.o \
- dialog.o
+ teenagent.o
# This module can be built as a plugin
ifeq ($(ENABLE_TEENAGENT), DYNAMIC_PLUGIN)
diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp
index bb0e9773a2..c30809eef4 100644
--- a/engines/teenagent/teenagent.cpp
+++ b/engines/teenagent/teenagent.cpp
@@ -618,9 +618,7 @@ Common::Error TeenAgentEngine::run() {
_system->updateScreen();
- if (console->isAttached()) {
- console->onFrame();
- }
+ console->onFrame();
uint32 next_tick = MIN(game_timer, mark_timer);
if (next_tick > 0) {
diff --git a/engines/tinsel/actors.cpp b/engines/tinsel/actors.cpp
index efcefb442c..3577f4e0cc 100644
--- a/engines/tinsel/actors.cpp
+++ b/engines/tinsel/actors.cpp
@@ -197,10 +197,8 @@ void RegisterActors(int num) {
}
void FreeActors() {
- if (actorInfo) {
- free(actorInfo);
- actorInfo = NULL;
- }
+ free(actorInfo);
+ actorInfo = NULL;
}
/**
diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp
index 938507c3f9..b13de103c0 100644
--- a/engines/tinsel/bmv.cpp
+++ b/engines/tinsel/bmv.cpp
@@ -730,16 +730,12 @@ void BMVPlayer::FinishBMV() {
stream.close();
// Release the data buffer
- if (bigBuffer != NULL) {
- free(bigBuffer);
- bigBuffer = NULL;
- }
+ free(bigBuffer);
+ bigBuffer = NULL;
// Release the screen buffer
- if (screenBuffer != NULL) {
- free(screenBuffer);
- screenBuffer = NULL;
- }
+ free(screenBuffer);
+ screenBuffer = NULL;
// Ditch any text objects
for (i = 0; i < 2; i++) {
diff --git a/engines/tinsel/cliprect.cpp b/engines/tinsel/cliprect.cpp
index 69a71b874f..5f287d3eab 100644
--- a/engines/tinsel/cliprect.cpp
+++ b/engines/tinsel/cliprect.cpp
@@ -28,17 +28,15 @@
#include "tinsel/graphics.h" // normal object drawing
#include "tinsel/object.h"
#include "tinsel/palette.h"
+#include "tinsel/tinsel.h" // for _vm
namespace Tinsel {
-/** list of all clip rectangles */
-static RectList s_rectList;
-
/**
* Resets the clipping rectangle allocator.
*/
void ResetClipRect() {
- s_rectList.clear();
+ _vm->_clipRects.clear();
}
/**
@@ -46,11 +44,11 @@ void ResetClipRect() {
* @param pClip clip rectangle dimensions to allocate
*/
void AddClipRect(const Common::Rect &pClip) {
- s_rectList.push_back(pClip);
+ _vm->_clipRects.push_back(pClip);
}
const RectList &GetClipRects() {
- return s_rectList;
+ return _vm->_clipRects;
}
/**
@@ -175,6 +173,8 @@ void FindMovingObjects(OBJECT *pObjList, Common::Point *pWin, Common::Rect *pCli
* the total number of clip rectangles.
*/
void MergeClipRect() {
+ RectList &s_rectList = _vm->_clipRects;
+
if (s_rectList.size() <= 1)
return;
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index 70a2f475ee..d6bdad6032 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -75,549 +75,7 @@ static const PlainGameDescriptor tinselGames[] = {
{0, 0}
};
-
-namespace Tinsel {
-
-using Common::GUIO_NONE;
-using Common::GUIO_NOSPEECH;
-using Common::GUIO_NOSFX;
-using Common::GUIO_NOMUSIC;
-
-static const TinselGameDescription gameDescriptions[] = {
-
- // Note: The following is the (hopefully) definitive list of version details:
- // TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
- // of the Tinsel engine and graphics compression
- // TINSEL_V1: There were two versions of the Discworld 1 game - the first used .GRA
- // files, and the second used .SCN files. The second also provided some fixes to
- // various script bugs and coding errors, but is still considered TINSEL_V1,
- // as both game versions work equally well with the newer code.
- // TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
- // and as far as we know there aren't any variations of this engine.
-
- { // Floppy Demo V0 from http://www.adventure-treff.de/specials/dl_demos.php
- {
- "dw",
- "Floppy Demo",
- AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
- //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSPEECH | GUIO_NOSFX | GUIO_NOMUSIC
- },
- GID_DW1,
- 0,
- GF_DEMO,
- TINSEL_V0,
- },
-
- { // CD Demo V1 version, with *.gra files
- {
- "dw",
- "CD Demo",
- {
- {"dw.gra", 0, "ef5a2518c9e205f786f5a4526396e661", 781676},
- {"english.smp", 0, NULL, -1},
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD,
- TINSEL_V1,
- },
-
- { // Multilingual Floppy V1 with *.gra files.
- // Note: It contains no english subtitles.
- {
- "dw",
- "Floppy",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NOSPEECH
- },
- GID_DW1,
- 0,
- GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Floppy V1 version, with *.gra files
- {
- "dw",
- "Floppy",
- AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- GID_DW1,
- 0,
- GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // CD V1 version, with *.gra files (same as the floppy one, with english.smp)
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Italian CD with english speech and *.gra files.
- // Note: It contains only italian subtitles, but inside english.txt
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 237774},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Multilingual CD with english speech and *.gra files.
- // Note: It contains no english subtitles.
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- {
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
- {
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
- {
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // English CD v2
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "70955425870c7720d6eebed903b2ef41", 776188},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Hebrew CD v2
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "759d1374b4f02af6d52fc07c96679936", 770780},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::HE_ISR,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Discworld PSX CD
- {
- "dw",
- "CD",
- {
- {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPSX,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // multilanguage PSX demo
- {
- "dw",
- "CD demo",
- {
- {"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},
- {"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942},
- {"italian.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 239834},
- {"spanish.txt", 0, "c324170c3f1922c605c5cc09ba265aa5", 236702},
- {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPSX,
- ADGF_DEMO,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V1,
- },
-
-#if 0
- { // English Saturn CD
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "6803f293c88758057cc685b9437f7637", 382248},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-#endif
-
-#if 0
- { // Mac multilanguage CD
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "cfc40a8d5d476a1c9d3abf826fa46f8c", 1265532},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
-#endif
-
- { // German CD re-release "Neon Edition"
- // Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
- {
- "dw",
- "CD",
- AD_ENTRY1s("dw.scn", "6182c7986eaec893c62fb6ea13a9f225", 774556),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Russian Discworld 1
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
- {"english.txt", 0, "f73dcbd7b136b37c2adf7c9448ea336d", 231821},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::RU_RUS,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // European/Australian Discworld 2 release
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_GRB,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // US Discworld 2 release
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"us1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // French version of Discworld 2
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"french1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // German Discworld 2 re-release "Neon Edition"
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"german1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // Italian/Spanish Discworld 2
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {"italian1.txt", 0, "d443249f8b55489b5888c227b9096f4e", 246495},
- {NULL, 0, NULL, 0}
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
- {
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {"spanish1.txt", 0, "bc6e147c5f542db228ac577357e4d897", 230323},
- {NULL, 0, NULL, 0}
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // Russian Discworld 2 release by Fargus
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {"english1.txt", 0, "b522e19d7b2cd7b85e50e36fe48e36a9", 274444},
- {NULL, 0, NULL, 0}
- },
- Common::RU_RUS,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
-};
-
-} // End of namespace Tinsel
+#include "tinsel/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -637,7 +95,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class TinselMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h
new file mode 100644
index 0000000000..b467cc613e
--- /dev/null
+++ b/engines/tinsel/detection_tables.h
@@ -0,0 +1,567 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+namespace Tinsel {
+
+using Common::GUIO_NONE;
+using Common::GUIO_NOSPEECH;
+using Common::GUIO_NOSFX;
+using Common::GUIO_NOMUSIC;
+
+static const TinselGameDescription gameDescriptions[] = {
+
+ // Note: The following is the (hopefully) definitive list of version details:
+ // TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
+ // of the Tinsel engine and graphics compression
+ // TINSEL_V1: There were two versions of the Discworld 1 game - the first used .GRA
+ // files, and the second used .SCN files. The second also provided some fixes to
+ // various script bugs and coding errors, but is still considered TINSEL_V1,
+ // as both game versions work equally well with the newer code.
+ // TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
+ // and as far as we know there aren't any variations of this engine.
+
+ { // Floppy Demo V0 from http://www.adventure-treff.de/specials/dl_demos.php
+ {
+ "dw",
+ "Floppy Demo",
+ AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
+ //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSPEECH | GUIO_NOSFX | GUIO_NOMUSIC
+ },
+ GID_DW1,
+ 0,
+ GF_DEMO,
+ TINSEL_V0,
+ },
+
+ { // CD Demo V1 version, with *.gra files
+ {
+ "dw",
+ "CD Demo",
+ {
+ {"dw.gra", 0, "ef5a2518c9e205f786f5a4526396e661", 781676},
+ {"english.smp", 0, NULL, -1},
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD,
+ TINSEL_V1,
+ },
+
+ { // Multilingual Floppy V1 with *.gra files.
+ // Note: It contains no english subtitles.
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Floppy V1 version, with *.gra files
+ {
+ "dw",
+ "Floppy",
+ AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // CD V1 version, with *.gra files (same as the floppy one, with english.smp)
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Italian CD with english speech and *.gra files.
+ // Note: It contains only italian subtitles, but inside english.txt
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 237774},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Multilingual CD with english speech and *.gra files.
+ // Note: It contains no english subtitles.
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ {
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+ {
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+ {
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // English CD v2
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "70955425870c7720d6eebed903b2ef41", 776188},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Hebrew CD v2
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "759d1374b4f02af6d52fc07c96679936", 770780},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::HE_ISR,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Discworld PSX CD
+ {
+ "dw",
+ "CD",
+ {
+ {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPSX,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // multilanguage PSX demo
+ {
+ "dw",
+ "CD demo",
+ {
+ {"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},
+ {"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942},
+ {"italian.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 239834},
+ {"spanish.txt", 0, "c324170c3f1922c605c5cc09ba265aa5", 236702},
+ {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPSX,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V1,
+ },
+
+#if 0
+ { // English Saturn CD
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "6803f293c88758057cc685b9437f7637", 382248},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+#endif
+
+#if 0
+ { // Mac multilanguage CD
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "cfc40a8d5d476a1c9d3abf826fa46f8c", 1265532},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+#endif
+
+ { // German CD re-release "Neon Edition"
+ // Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
+ {
+ "dw",
+ "CD",
+ AD_ENTRY1s("dw.scn", "6182c7986eaec893c62fb6ea13a9f225", 774556),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Russian Discworld 1
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
+ {"english.txt", 0, "f73dcbd7b136b37c2adf7c9448ea336d", 231821},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // European/Australian Discworld 2 release
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_GRB,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // US Discworld 2 release
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"us1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // French version of Discworld 2
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"french1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // German Discworld 2 re-release "Neon Edition"
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"german1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // Italian/Spanish Discworld 2
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {"italian1.txt", 0, "d443249f8b55489b5888c227b9096f4e", 246495},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+ {
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {"spanish1.txt", 0, "bc6e147c5f542db228ac577357e4d897", 230323},
+ {NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // Russian Discworld 2 release by Fargus
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {"english1.txt", 0, "b522e19d7b2cd7b85e50e36fe48e36a9", 274444},
+ {NULL, 0, NULL, 0}
+ },
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
+};
+
+} // End of namespace Tinsel
diff --git a/engines/tinsel/graphics.cpp b/engines/tinsel/graphics.cpp
index 9700e8947f..48270d94e3 100644
--- a/engines/tinsel/graphics.cpp
+++ b/engines/tinsel/graphics.cpp
@@ -443,6 +443,12 @@ static void t2WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool apply
int numBytes;
int clipAmount;
+ // WORKAROUND: One of the mortician frames has several corrupt bytes in the Russian version
+ if ((pObj->hBits == 2517583660UL) && (_vm->getLanguage() == Common::RU_RUS)) {
+ uint8 correctBytes[5] = {0xA3, 0x00, 0x89, 0xC0, 0xA6};
+ Common::copy(&correctBytes[0], &correctBytes[5], srcP);
+ }
+
for (int y = 0; y < pObj->height; ++y) {
// Get the position to start writing out from
uint8 *tempP = !horizFlipped ? destP :
@@ -596,6 +602,23 @@ static void PackedWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP,
int numBytes, colour;
int v;
+ if (_vm->getLanguage() == Common::RU_RUS) {
+ // WORKAROUND: One of the mortician frames has several corrupt bytes in the Russian version
+ if (pObj->hBits == 2517583393UL) {
+ uint8 correctBytes[5] = {0x00, 0x00, 0x17, 0x01, 0x00};
+ Common::copy(&correctBytes[0], &correctBytes[5], srcP + 267);
+ }
+ // WORKAROUND: One of Dibbler's frames in the end sequence has corrupt bytes in the Russian version
+ if (pObj->hBits == 33651742) {
+ uint8 correctBytes[40] = {
+ 0x06, 0xc0, 0xd6, 0xc1, 0x09, 0xce, 0x0d, 0x24, 0x02, 0x12, 0x01, 0x00, 0x00, 0x23, 0x21, 0x32,
+ 0x12, 0x00, 0x00, 0x20, 0x01, 0x11, 0x32, 0x12, 0x01, 0x00, 0x00, 0x1b, 0x02, 0x11, 0x34, 0x11,
+ 0x00, 0x00, 0x18, 0x01, 0x11, 0x35, 0x21, 0x01
+ };
+ Common::copy(&correctBytes[0], &correctBytes[40], srcP);
+ }
+ }
+
if (applyClipping) {
pObj->height -= pObj->botClip;
topClip = pObj->topClip;
diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp
index de573feee2..df686f8bee 100644
--- a/engines/tinsel/handle.cpp
+++ b/engines/tinsel/handle.cpp
@@ -175,22 +175,18 @@ void SetupHandleTable() {
}
void FreeHandleTable() {
- if (handleTable) {
- free(handleTable);
- handleTable = NULL;
- }
- if (cdGraphStream) {
- delete cdGraphStream;
- cdGraphStream = 0;
- }
+ free(handleTable);
+ handleTable = NULL;
+
+ delete cdGraphStream;
+ cdGraphStream = NULL;
}
/**
* Loads a memory block as a file.
*/
void OpenCDGraphFile() {
- if (cdGraphStream)
- delete cdGraphStream;
+ delete cdGraphStream;
// As the theory goes, the right CD will be in there!
diff --git a/engines/tinsel/object.cpp b/engines/tinsel/object.cpp
index f91e37a063..7a93a0b30a 100644
--- a/engines/tinsel/object.cpp
+++ b/engines/tinsel/object.cpp
@@ -49,10 +49,8 @@ static int maxObj = 0;
#endif
void FreeObjectList() {
- if (objectList) {
- free(objectList);
- objectList = NULL;
- }
+ free(objectList);
+ objectList = NULL;
}
/**
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index c472a770d2..98fb078459 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -148,6 +148,7 @@ static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491),
OP_ONE, OP_LIBCALL | OPSIZE8, 14, // Re-show the cursor
OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle
OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)};
+static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)};
#undef FRAGMENT_WORD
@@ -207,6 +208,12 @@ const WorkaroundEntry workaroundList[] = {
// See bug report #2934211.
{TINSEL_V1, true, 352601285, 1569, sizeof(fragment11), fragment11},
{TINSEL_V1, false, 352602304, 1488, sizeof(fragment12), fragment12},
+
+ // DW2: Corrects a bug with global 306 not being cleared if you leave
+ // the marketplace scene whilst D'Blah is talking (even if it's not
+ // actually audible); returning to the scene and clicking on him multiple
+ // times would cause the game to crash
+ {TINSEL_V2, true, 1109294728, 0, sizeof(fragment13), fragment13},
{TINSEL_V0, false, 0, 0, 0, NULL}
};
diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp
index b90ace4613..b010ad1fcb 100644
--- a/engines/tinsel/saveload.cpp
+++ b/engines/tinsel/saveload.cpp
@@ -180,7 +180,8 @@ static void syncSavedMover(Common::Serializer &s, SAVED_MOVER &sm) {
static void syncSavedActor(Common::Serializer &s, SAVED_ACTOR &sa) {
s.syncAsUint16LE(sa.actorID);
s.syncAsUint16LE(sa.zFactor);
- s.syncAsUint32LE(sa.bAlive);
+ s.syncAsUint16LE(sa.bAlive);
+ s.syncAsUint16LE(sa.bHidden);
s.syncAsUint32LE(sa.presFilm);
s.syncAsUint16LE(sa.presRnum);
s.syncAsUint16LE(sa.presPlayX);
diff --git a/engines/tinsel/savescn.cpp b/engines/tinsel/savescn.cpp
index 50231d34bb..2b5c815d3c 100644
--- a/engines/tinsel/savescn.cpp
+++ b/engines/tinsel/savescn.cpp
@@ -162,10 +162,8 @@ void InitialiseSaveScenes() {
}
void FreeSaveScenes() {
- if (ssData) {
- free(ssData);
- ssData = NULL;
- }
+ free(ssData);
+ ssData = NULL;
}
/**
diff --git a/engines/tinsel/strres.cpp b/engines/tinsel/strres.cpp
index 8f9f72f446..2416d6a8fa 100644
--- a/engines/tinsel/strres.cpp
+++ b/engines/tinsel/strres.cpp
@@ -88,11 +88,9 @@ void ChangeLanguage(LANGUAGE newLang) {
textLanguage = newLang;
sampleLanguage = newLang;
- if (textBuffer) {
- // free the previous buffer
- free(textBuffer);
- textBuffer = NULL;
- }
+ // free the previous buffer
+ free(textBuffer);
+ textBuffer = NULL;
// Try and open the specified language file. If it fails, and the language
// isn't English, try falling back on opening 'english.txt' - some foreign
@@ -355,10 +353,8 @@ int SubStringCount(int id) {
void FreeTextBuffer() {
- if (textBuffer) {
- free(textBuffer);
- textBuffer = NULL;
- }
+ free(textBuffer);
+ textBuffer = NULL;
}
/**
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index 677392a35d..766d4ed54a 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -5557,7 +5557,7 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
case WALKED: {
// Common to both DW1 & DW2
pp -= 3; // 4 parameters
- bool tmp;
+ bool tmp = false;
Walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape, tmp);
if (!coroParam) {
// Only write the result to the stack if walked actually completed running.
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 8d11efef3c..6c77a98dda 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -856,11 +856,11 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
if (cd_num >= 0)
_system->openCD(cd_num);
- MidiDriverType 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::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ //bool adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (native_mt32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
@@ -1001,8 +1001,7 @@ Common::Error TinselEngine::run() {
uint32 timerVal = 0;
while (!shouldQuit()) {
assert(_console);
- if (_console->isAttached())
- _console->onFrame();
+ _console->onFrame();
// Check for time to do next game cycle
if ((g_system->getMillis() > timerVal + GAME_FRAME_DELAY)) {
diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h
index 0e1d705815..df27a1e0e1 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -61,6 +61,8 @@ class PCMMusicPlayer;
class Scheduler;
class SoundManager;
+typedef Common::List<Common::Rect> RectList;
+
enum TinselGameID {
GID_DW1 = 0,
GID_DW2 = 1
@@ -203,6 +205,10 @@ public:
/** Stack of pending keypresses. */
Common::List<Common::Event> _keypresses;
+
+ /** List of all clip rectangles. */
+ RectList _clipRects;
+
private:
//MidiMusicPlayer *_midiMusic;
int _musicVolume;
diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp
index 65a6a29bcc..35f03fa657 100644
--- a/engines/touche/detection.cpp
+++ b/engines/touche/detection.cpp
@@ -124,6 +124,11 @@ static const ADFileBasedFallback fileBasedFallback[] = {
} // End of namespace Touche
+static const char *directoryGlobs[] = {
+ "database",
+ 0
+};
+
static const ADParams detectionParams = {
(const byte *)Touche::gameDescriptions,
sizeof(ADGameDescription),
@@ -134,7 +139,11 @@ static const ADParams detectionParams = {
Touche::fileBasedFallback, // file-based detection data to enable not yet known versions to start
kADFlagPrintWarningOnFileBasedFallback,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 2,
+ // List of directory globs
+ directoryGlobs
};
class ToucheMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/touche/midi.cpp b/engines/touche/midi.cpp
index 9dbef4d76f..439d3b9ac2 100644
--- a/engines/touche/midi.cpp
+++ b/engines/touche/midi.cpp
@@ -92,9 +92,9 @@ void MidiPlayer::setVolume(int volume) {
}
int MidiPlayer::open() {
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
- _driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ _driver = MidiDriver::createMidi(dev);
int ret = _driver->open();
if (ret == 0) {
_parser = MidiParser::createParser_SMF();
diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp
index 187e685d06..2dc8b76b4f 100644
--- a/engines/touche/touche.cpp
+++ b/engines/touche/touche.cpp
@@ -28,6 +28,8 @@
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/EventRecorder.h"
+#include "common/file.h"
+#include "common/fs.h"
#include "common/system.h"
#include "engines/util.h"
@@ -70,6 +72,10 @@ ToucheEngine::ToucheEngine(OSystem *system, Common::Language language)
_menuRedrawCounter = 0;
memset(_paletteBuffer, 0, sizeof(_paletteBuffer));
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+
+ SearchMan.addSubDirectoryMatching(gameDataDir, "database");
+
DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
DebugMan.addDebugChannel(kDebugGraphics, "Graphics", "Graphics debug level");
DebugMan.addDebugChannel(kDebugResource, "Resource", "Resource debug level");
diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp
index b4f30cb7fd..0a9dec9b46 100644
--- a/engines/tucker/detection.cpp
+++ b/engines/tucker/detection.cpp
@@ -114,7 +114,9 @@ static const ADParams detectionParams = {
"tucker",
0,
0,
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ 1,
+ 0
};
static const ADGameDescription tuckerDemoGameDescription = {
diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp
index 68f5301a80..633ed2790d 100644
--- a/engines/tucker/sequences.cpp
+++ b/engines/tucker/sequences.cpp
@@ -491,7 +491,6 @@ AnimationSequencePlayer::AnimationSequencePlayer(OSystem *system, Audio::Mixer *
_offscreenBuffer = (uint8 *)malloc(kScreenWidth * kScreenHeight);
_updateScreenWidth = 0;
_updateScreenPicture = false;
- _updateScreenOffset = 0;
_picBufPtr = _pic2BufPtr = 0;
}
@@ -537,9 +536,9 @@ void AnimationSequencePlayer::mainLoop() {
}
// budttle2.flc is shorter in french version ; start the background music
// earlier and skip any sounds effects
- if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 127) {
+ if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 126) {
_soundSeqDataIndex = 6;
- _frameCounter = 79;
+ _frameCounter = 80;
}
}
(this->*(_updateFunc[_updateFuncIndex].play))();
@@ -765,10 +764,10 @@ void AnimationSequencePlayer::openAnimation(int index, const char *fileName) {
}
}
-bool AnimationSequencePlayer::decodeNextAnimationFrame(int index) {
+bool AnimationSequencePlayer::decodeNextAnimationFrame(int index, bool copyDirtyRects) {
::Graphics::Surface *surface = _flicPlayer[index].decodeNextFrame();
- if (_seqNum == 19) {
+ if (!copyDirtyRects) {
for (uint16 y = 0; (y < surface->h) && (y < kScreenHeight); y++)
memcpy(_offscreenBuffer + y * kScreenWidth, (byte *)surface->pixels + y * surface->pitch, surface->w);
} else {
@@ -807,13 +806,13 @@ void AnimationSequencePlayer::playIntroSeq19_20() {
// cogs, and is being replayed when an intro credit appears
::Graphics::Surface *surface = 0;
- if (_flicPlayer[0].getCurFrame() >= 117) {
+ if (_flicPlayer[0].getCurFrame() >= 115) {
surface = _flicPlayer[1].decodeNextFrame();
if (_flicPlayer[1].endOfVideo())
_flicPlayer[1].reset();
}
- bool framesLeft = decodeNextAnimationFrame(0);
+ bool framesLeft = decodeNextAnimationFrame(0, false);
if (surface)
for (int i = 0; i < kScreenWidth * kScreenHeight; ++i)
@@ -841,19 +840,28 @@ void AnimationSequencePlayer::displayLoadingScreen() {
void AnimationSequencePlayer::initPicPart4() {
_updateScreenWidth = 320;
_updateScreenPicture = true;
- _updateScreenOffset = 0;
+ _updateScreenCounter = 0;
+ _updateScreenIndex = -1;
}
void AnimationSequencePlayer::drawPicPart4() {
- static const uint8 offsetsTable[77] = {
- 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
- 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3,
- 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1
- };
- _updateScreenWidth = _updateScreenWidth - offsetsTable[_updateScreenOffset];
- ++_updateScreenOffset;
+ static const uint8 offsets[] = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 };
+ if (_updateScreenIndex == -1) {
+ for (int i = 0; i < 256; ++i) {
+ if (memcmp(_animationPalette + i * 4, _picBufPtr + 32 + i * 3, 3) != 0) {
+ memcpy(_animationPalette + i * 4, _picBufPtr + 32 + i * 3, 3);
+ _animationPalette[i * 4 + 3] = 0;
+ }
+ }
+ }
+ if (_updateScreenCounter == 0) {
+ static const uint8 counter[] = { 1, 2, 3, 4, 5, 35, 5, 4, 3, 2, 1 };
+ ++_updateScreenIndex;
+ assert(_updateScreenIndex < ARRAYSIZE(counter));
+ _updateScreenCounter = counter[_updateScreenIndex];
+ }
+ --_updateScreenCounter;
+ _updateScreenWidth -= offsets[_updateScreenIndex];
for (int y = 0; y < 200; ++y) {
memcpy(_offscreenBuffer + y * 320, _picBufPtr + 800 + y * 640 + _updateScreenWidth, 320);
}
@@ -875,7 +883,7 @@ void AnimationSequencePlayer::loadIntroSeq3_4() {
void AnimationSequencePlayer::playIntroSeq3_4() {
if (!_updateScreenPicture) {
bool framesLeft = decodeNextAnimationFrame(0);
- if (_flicPlayer[0].getCurFrame() == 707) {
+ if (_flicPlayer[0].getCurFrame() == 705) {
initPicPart4();
}
if (!framesLeft) {
@@ -914,17 +922,10 @@ void AnimationSequencePlayer::drawPic2Part10() {
}
void AnimationSequencePlayer::drawPic1Part10() {
- ::Graphics::Surface *surface = _flicPlayer[0].decodeNextFrame();
- _flicPlayer[0].copyDirtyRectsToBuffer(_offscreenBuffer, kScreenWidth);
- ++_frameCounter;
-
- if (_flicPlayer[0].hasDirtyPalette())
- getRGBPalette(0);
-
int offset = 0;
for (int y = 0; y < kScreenHeight; ++y) {
for (int x = 0; x < kScreenWidth; ++x) {
- byte color = *((byte *)surface->pixels + offset);
+ byte color = _offscreenBuffer[offset];
if (color == 0)
color = _picBufPtr[800 + y * 640 + _updateScreenWidth + x];
@@ -943,22 +944,24 @@ void AnimationSequencePlayer::loadIntroSeq9_10() {
}
void AnimationSequencePlayer::playIntroSeq9_10() {
- if (_flicPlayer[0].getCurFrame() >= 265 && _flicPlayer[0].getCurFrame() <= 296) {
+ const int nextFrame = _flicPlayer[0].getCurFrame() + 1;
+ if (nextFrame >= 263 && nextFrame <= 294) {
+ decodeNextAnimationFrame(0, false);
drawPic1Part10();
_updateScreenWidth += 6;
- } else if (_flicPlayer[0].getCurFrame() == 985) {
+ } else if (nextFrame == 983) {
decodeNextAnimationFrame(0);
drawPic2Part10();
- } else if (_flicPlayer[0].getCurFrame() >= 989 && _flicPlayer[0].getCurFrame() <= 997) {
+ } else if (nextFrame >= 987 && nextFrame <= 995) {
+ decodeNextAnimationFrame(0, false);
drawPic1Part10();
_updateScreenWidth -= 25;
if (_updateScreenWidth < 0) {
_updateScreenWidth = 0;
}
- }
-
- if (_flicPlayer[0].endOfVideo())
+ } else if (!decodeNextAnimationFrame(0)) {
_changeToNextSequence = true;
+ }
}
void AnimationSequencePlayer::loadIntroSeq21_22() {
diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h
index d9810c7929..86f5843e77 100644
--- a/engines/tucker/tucker.h
+++ b/engines/tucker/tucker.h
@@ -934,7 +934,7 @@ private:
void unloadAnimation();
uint8 *loadPicture(const char *fileName);
void openAnimation(int index, const char *fileName);
- bool decodeNextAnimationFrame(int index);
+ bool decodeNextAnimationFrame(int index, bool copyDirtyRects = true);
void loadIntroSeq17_18();
void playIntroSeq17_18();
void loadIntroSeq19_20();
@@ -975,7 +975,8 @@ private:
uint8 *_offscreenBuffer;
int _updateScreenWidth;
int _updateScreenPicture;
- int _updateScreenOffset;
+ int _updateScreenCounter;
+ int _updateScreenIndex;
int _frameCounter;
int _frameTime;
uint32 _lastFrameTime;